@zackbart/connecta 0.34.1 → 0.35.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 (429) hide show
  1. package/CHANGELOG.md +363 -0
  2. package/README.md +62 -106
  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 +2051 -1160
  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 +663 -271
  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 +12 -7
  53. package/dist/connectors/api-connector.js +142 -56
  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 +228 -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 +383 -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/agents.d.ts +5 -4
  109. package/dist/operator-ui/agents.js +17 -7
  110. package/dist/operator-ui/config-summary.js +5 -4
  111. package/dist/operator-ui/connection-map.js +7 -5
  112. package/dist/operator-ui/contract.d.ts +7 -3
  113. package/dist/operator-ui/endpoints.js +4 -13
  114. package/dist/operator-ui/fix-prompts.d.ts +1 -1
  115. package/dist/operator-ui/fix-prompts.js +0 -8
  116. package/dist/operator-ui/generated.js +3 -3
  117. package/dist/operator-ui/model.d.ts +2 -3
  118. package/dist/operator-ui/overview.js +5 -2
  119. package/dist/operator-ui/setup-commands.js +0 -4
  120. package/dist/operator-ui/view.d.ts +2 -25
  121. package/dist/operator-ui/view.js +2 -76
  122. package/dist/program-writes.js +47 -26
  123. package/dist/provider-call-facts.d.ts +115 -0
  124. package/dist/provider-call-facts.js +182 -0
  125. package/dist/provider-call.d.ts +112 -0
  126. package/dist/provider-call.js +271 -0
  127. package/dist/provider.d.ts +65 -31
  128. package/dist/provider.js +116 -39
  129. package/dist/providers/_shared/google/hatch.js +91 -0
  130. package/dist/providers/_shared/google/kit.js +537 -0
  131. package/dist/providers/_shared/google/result-size.js +15 -2
  132. package/dist/providers/_shared/google/service-account.d.ts +2 -1
  133. package/dist/providers/_shared/google/service-account.js +8 -4
  134. package/dist/providers/_shared/google/workspace.d.ts +6 -210
  135. package/dist/providers/_shared/google/workspace.js +6 -425
  136. package/dist/providers/_shared/rest/compact-index.js +86 -0
  137. package/dist/providers/_shared/rest/dispatch.js +3 -23
  138. package/dist/providers/_shared/rest/kit.js +546 -0
  139. package/dist/providers/_shared/rest/operation-index.js +230 -14
  140. package/dist/providers/_shared/rest/response-media.js +11 -0
  141. package/dist/providers/_shared/rest/tools.js +101 -78
  142. package/dist/providers/_shared/rest/value-safety.js +353 -195
  143. package/dist/providers/_shared/sensitive-fields.js +27 -0
  144. package/dist/providers/breeze/api.js +584 -0
  145. package/dist/providers/breeze/index.d.ts +1 -3
  146. package/dist/providers/breeze/index.js +67 -1420
  147. package/dist/providers/breeze/openapi.generated.js +60 -0
  148. package/dist/providers/breeze/skill.generated.js +2 -6
  149. package/dist/providers/breeze/tools.js +696 -0
  150. package/dist/providers/ccb/api.js +496 -0
  151. package/dist/providers/ccb/index.d.ts +15 -37
  152. package/dist/providers/ccb/index.js +77 -1702
  153. package/dist/providers/ccb/lib/constants.d.ts +34 -0
  154. package/dist/providers/ccb/lib/constants.js +74 -0
  155. package/dist/providers/ccb/lib/guide.js +9 -0
  156. package/dist/providers/ccb/lib/verdicts.js +387 -0
  157. package/dist/providers/ccb/openapi.generated.js +397 -0
  158. package/dist/providers/ccb/safety.js +295 -0
  159. package/dist/providers/ccb/skill.generated.js +2 -6
  160. package/dist/providers/ccb/tools.js +853 -0
  161. package/dist/providers/cloudflare/api.js +1066 -0
  162. package/dist/providers/cloudflare/index.d.ts +5 -22
  163. package/dist/providers/cloudflare/index.js +79 -197
  164. package/dist/providers/cloudflare/lib/config.d.ts +16 -0
  165. package/dist/providers/cloudflare/lib/config.js +2 -0
  166. package/dist/providers/cloudflare/lib/discovery.js +29 -0
  167. package/dist/providers/cloudflare/{graphql.js → lib/graphql.js} +1 -1
  168. package/dist/providers/cloudflare/lib/hatch.js +103 -0
  169. package/dist/providers/cloudflare/lib/path-bounds.js +28 -0
  170. package/dist/providers/cloudflare/lib/response-media.js +75 -0
  171. package/dist/providers/cloudflare/openapi.generated.js +4 -3
  172. package/dist/providers/cloudflare/{value-safety.js → safety.js} +487 -0
  173. package/dist/providers/cloudflare/skill.generated.js +2 -10
  174. package/dist/providers/cloudflare/tools.js +360 -0
  175. package/dist/providers/docs/api.js +51 -0
  176. package/dist/providers/docs/index.d.ts +1 -12
  177. package/dist/providers/docs/index.js +53 -1165
  178. package/dist/providers/docs/lib/constants.d.ts +18 -0
  179. package/dist/providers/docs/lib/constants.js +18 -0
  180. package/dist/providers/docs/lib/discovery.js +26 -0
  181. package/dist/providers/docs/lib/requests.js +48 -0
  182. package/dist/providers/docs/lib/schemas.js +168 -0
  183. package/dist/providers/docs/openapi.generated.js +16 -0
  184. package/dist/providers/docs/safety.js +54 -0
  185. package/dist/providers/docs/skill.generated.js +2 -6
  186. package/dist/providers/docs/tools.js +802 -0
  187. package/dist/providers/drive/api.js +177 -0
  188. package/dist/providers/drive/index.d.ts +1 -12
  189. package/dist/providers/drive/index.js +38 -1448
  190. package/dist/providers/drive/lib/constants.d.ts +6 -0
  191. package/dist/providers/drive/lib/constants.js +6 -0
  192. package/dist/providers/drive/lib/contracts.js +532 -0
  193. package/dist/providers/drive/lib/hatch.js +197 -0
  194. package/dist/providers/drive/openapi.generated.js +77 -0
  195. package/dist/providers/drive/safety.js +90 -0
  196. package/dist/providers/drive/skill.generated.js +2 -6
  197. package/dist/providers/drive/tools.js +1290 -0
  198. package/dist/providers/forms/api.js +71 -0
  199. package/dist/providers/forms/index.d.ts +1 -12
  200. package/dist/providers/forms/index.js +49 -1145
  201. package/dist/providers/forms/lib/constants.d.ts +14 -0
  202. package/dist/providers/forms/lib/constants.js +19 -0
  203. package/dist/providers/forms/lib/hatch.js +72 -0
  204. package/dist/providers/forms/openapi.generated.js +23 -0
  205. package/dist/providers/forms/safety.js +34 -0
  206. package/dist/providers/forms/skill.generated.js +2 -8
  207. package/dist/providers/forms/tools.js +1024 -0
  208. package/dist/providers/github/api.js +150 -0
  209. package/dist/providers/github/index.d.ts +4 -5
  210. package/dist/providers/github/index.js +30 -87
  211. package/dist/providers/github/{auth.d.ts → lib/auth.d.ts} +5 -3
  212. package/dist/providers/github/{auth.js → lib/auth.js} +40 -43
  213. package/dist/providers/github/lib/discovery.js +132 -0
  214. package/dist/providers/github/lib/operations.js +58 -0
  215. package/dist/providers/github/{scope.d.ts → lib/scope.d.ts} +2 -0
  216. package/dist/providers/github/{scope.js → lib/scope.js} +25 -1
  217. package/dist/providers/github/openapi.generated.js +1216 -0
  218. package/dist/providers/github/safety.js +216 -0
  219. package/dist/providers/github/skill.generated.js +2 -5
  220. package/dist/providers/github/tools.js +506 -0
  221. package/dist/providers/gmail/api.js +114 -0
  222. package/dist/providers/gmail/index.d.ts +1 -12
  223. package/dist/providers/gmail/index.js +73 -1621
  224. package/dist/providers/gmail/lib/constants.d.ts +13 -0
  225. package/dist/providers/gmail/lib/constants.js +16 -0
  226. package/dist/providers/gmail/lib/index.js +173 -0
  227. package/dist/providers/gmail/lib/schemas.js +176 -0
  228. package/dist/providers/gmail/lib/threading.js +120 -0
  229. package/dist/providers/gmail/openapi.generated.js +92 -0
  230. package/dist/providers/gmail/safety.js +21 -0
  231. package/dist/providers/gmail/skill.generated.js +2 -6
  232. package/dist/providers/gmail/tools.js +1435 -0
  233. package/dist/providers/overflow/api.js +1008 -0
  234. package/dist/providers/overflow/index.d.ts +1 -17
  235. package/dist/providers/overflow/index.js +60 -1488
  236. package/dist/providers/overflow/skill.generated.js +2 -5
  237. package/dist/providers/overflow/tools.js +873 -0
  238. package/dist/providers/planning-center/api.js +467 -0
  239. package/dist/providers/planning-center/index.d.ts +4 -35
  240. package/dist/providers/planning-center/index.js +69 -2445
  241. package/dist/providers/planning-center/lib/constants.d.ts +58 -0
  242. package/dist/providers/planning-center/lib/constants.js +83 -0
  243. package/dist/providers/planning-center/lib/discovery.js +40 -0
  244. package/dist/providers/planning-center/lib/guide.js +39 -0
  245. package/dist/providers/planning-center/lib/json-api.js +83 -0
  246. package/dist/providers/planning-center/lib/verdicts.js +1758 -0
  247. package/dist/providers/planning-center/openapi.generated.js +1764 -0
  248. package/dist/providers/planning-center/safety.js +188 -0
  249. package/dist/providers/planning-center/skill.generated.js +2 -27
  250. package/dist/providers/planning-center/tools.js +1010 -0
  251. package/dist/providers/sheets/api.js +133 -0
  252. package/dist/providers/sheets/index.d.ts +4 -16
  253. package/dist/providers/sheets/index.js +61 -1172
  254. package/dist/providers/sheets/lib/constants.d.ts +14 -0
  255. package/dist/providers/sheets/lib/constants.js +14 -0
  256. package/dist/providers/sheets/lib/hatch.js +163 -0
  257. package/dist/providers/sheets/lib/requests.js +105 -0
  258. package/dist/providers/sheets/lib/schemas.js +138 -0
  259. package/dist/providers/sheets/openapi.generated.js +30 -0
  260. package/dist/providers/sheets/safety.js +27 -0
  261. package/dist/providers/sheets/skill.generated.js +2 -7
  262. package/dist/providers/sheets/tools.js +734 -0
  263. package/dist/providers/slides/api.js +147 -0
  264. package/dist/providers/slides/index.d.ts +2 -10
  265. package/dist/providers/slides/index.js +48 -2555
  266. package/dist/providers/slides/lib/constants.d.ts +11 -0
  267. package/dist/providers/slides/lib/constants.js +11 -0
  268. package/dist/providers/slides/lib/requests.js +60 -0
  269. package/dist/providers/slides/lib/schemas.js +565 -0
  270. package/dist/providers/slides/openapi.generated.js +18 -0
  271. package/dist/providers/slides/skill.generated.js +2 -6
  272. package/dist/providers/slides/tools.js +1968 -0
  273. package/dist/providers/stripe/api.js +401 -0
  274. package/dist/providers/stripe/index.d.ts +5 -27
  275. package/dist/providers/stripe/index.js +74 -210
  276. package/dist/providers/stripe/lib/config.d.ts +4 -0
  277. package/dist/providers/stripe/lib/config.js +2 -0
  278. package/dist/providers/stripe/openapi.generated.js +4 -3
  279. package/dist/providers/stripe/{value-safety.js → safety.js} +5 -5
  280. package/dist/providers/stripe/skill.generated.js +2 -11
  281. package/dist/providers/stripe/tools.js +77 -0
  282. package/dist/providers/tithely/api.js +531 -0
  283. package/dist/providers/tithely/index.d.ts +17 -0
  284. package/dist/providers/tithely/index.js +86 -965
  285. package/dist/providers/tithely/skill.generated.js +2 -5
  286. package/dist/providers/tithely/tools.js +426 -0
  287. package/dist/providers/vercel/api.js +551 -0
  288. package/dist/providers/vercel/index.d.ts +9 -25
  289. package/dist/providers/vercel/index.js +66 -260
  290. package/dist/providers/vercel/lib/config.d.ts +2 -0
  291. package/dist/providers/vercel/lib/config.js +2 -0
  292. package/dist/providers/vercel/lib/index.js +117 -0
  293. package/dist/providers/vercel/lib/logs.js +100 -0
  294. package/dist/providers/vercel/lib/pin.js +270 -0
  295. package/dist/providers/vercel/openapi.generated.js +10 -5
  296. package/dist/providers/vercel/{value-safety.js → safety.js} +247 -38
  297. package/dist/providers/vercel/skill.generated.js +2 -7
  298. package/dist/providers/vercel/tools.js +642 -0
  299. package/dist/redaction-identity.js +47 -0
  300. package/dist/redaction-marker.js +125 -0
  301. package/dist/registry.d.ts +21 -15
  302. package/dist/registry.js +409 -376
  303. package/dist/routes/access-tokens.js +1 -1
  304. package/dist/routes/actor-labels.js +1 -1
  305. package/dist/routes/connect.js +1 -1
  306. package/dist/routes/dispatch.js +103 -0
  307. package/dist/routes/mcp.js +170 -79
  308. package/dist/routes/oauth.js +3 -1
  309. package/dist/routes/operator.js +2 -2
  310. package/dist/routes/shared.d.ts +5 -4
  311. package/dist/routes/shared.js +244 -177
  312. package/dist/routes/skills.js +10 -8
  313. package/dist/routes/ui-config.js +27 -3
  314. package/dist/routes/ui-data.js +21 -20
  315. package/dist/routes/ui.js +1 -1
  316. package/dist/runtime/call-admission.js +0 -26
  317. package/dist/runtime/call-entry.js +30 -0
  318. package/dist/runtime/call.js +556 -0
  319. package/dist/runtime/connector-scope.js +7 -0
  320. package/dist/runtime/credential-probe.js +34 -0
  321. package/dist/runtime/executor.js +40 -0
  322. package/dist/runtime/native-storage.js +63 -0
  323. package/dist/runtime/registry.js +27 -0
  324. package/dist/runtime/request-scope.js +72 -0
  325. package/dist/runtime/run.js +15 -7
  326. package/dist/runtime/shared-read.js +34 -5
  327. package/dist/runtime/storage.js +34 -21
  328. package/dist/sent-secrets.d.ts +194 -37
  329. package/dist/sent-secrets.js +1743 -340
  330. package/dist/server.js +5 -56
  331. package/dist/skills.js +322 -222
  332. package/dist/sqlite.js +15 -14
  333. package/dist/storage/d1-copy.js +255 -0
  334. package/dist/storage/d1-driver.js +15 -0
  335. package/dist/storage/keys.js +28 -0
  336. package/dist/storage/sql-driver.js +88 -0
  337. package/dist/storage/sql-native.js +544 -0
  338. package/dist/storage/sql-schema.d.ts +6 -0
  339. package/dist/storage/sql-schema.js +15 -0
  340. package/dist/storage/sql.d.ts +14 -20
  341. package/dist/storage/sql.js +29 -535
  342. package/dist/timeout.js +0 -26
  343. package/dist/tool-safety.js +20 -10
  344. package/dist/types.d.ts +7 -109
  345. package/dist/upward.js +3 -0
  346. package/dist/usage-guide.js +3 -1
  347. package/dist/validate.d.ts +4 -2
  348. package/dist/validate.js +86 -9
  349. package/dist/version.d.ts +1 -1
  350. package/dist/version.js +1 -1
  351. package/dist/worker.js +312 -292
  352. package/dist/workspace-subject.js +5 -1
  353. package/dist/write-outcome.js +454 -34
  354. package/documentation/README.md +27 -7
  355. package/documentation/architecture.md +2547 -226
  356. package/documentation/auth.md +821 -98
  357. package/documentation/code-mode.md +49 -16
  358. package/documentation/deploying.md +164 -20
  359. package/documentation/hosted-mcp-migration.md +460 -0
  360. package/documentation/integrating.md +129 -144
  361. package/documentation/meta-tools.md +409 -32
  362. package/documentation/operating.md +27 -3
  363. package/documentation/operator-tests.md +3 -0
  364. package/documentation/operator-ui.md +68 -45
  365. package/documentation/provider-auth-migration.md +16 -63
  366. package/documentation/provider-migration-0.29.md +10 -5
  367. package/documentation/provider-migration-0.35.md +975 -0
  368. package/documentation/providers.md +965 -0
  369. package/examples/worker/AGENTS.md +23 -9
  370. package/examples/worker/README.md +254 -135
  371. package/examples/worker/alchemy.run.ts +116 -0
  372. package/examples/worker/gitignore +10 -0
  373. package/examples/worker/src/connecta.config.ts +23 -21
  374. package/examples/worker/wrangler.jsonc +14 -5
  375. package/guides/connecting-agents.md +157 -0
  376. package/guides/how-it-works.md +156 -0
  377. package/guides/safety-and-trust.md +153 -0
  378. package/package.json +27 -46
  379. package/templates/node/.env.example +3 -0
  380. package/templates/node/README.md +22 -2
  381. package/templates/node/docker-compose.yml +1 -0
  382. package/templates/node/package.json +3 -2
  383. package/templates/node/src/connecta.config.ts +8 -0
  384. package/dist/catalog-drift.d.ts +0 -138
  385. package/dist/catalog-drift.js +0 -578
  386. package/dist/providers/_shared/request-token.js +0 -83
  387. package/dist/providers/_shared/rest/operation-index.d.ts +0 -133
  388. package/dist/providers/_shared/rest/tools.d.ts +0 -141
  389. package/dist/providers/_shared/rest/value-safety.d.ts +0 -201
  390. package/dist/providers/basecamp/index.d.ts +0 -45
  391. package/dist/providers/basecamp/index.js +0 -997
  392. package/dist/providers/basecamp/skill.generated.js +0 -9
  393. package/dist/providers/cloudflare/rest.d.ts +0 -37
  394. package/dist/providers/cloudflare/rest.js +0 -1097
  395. package/dist/providers/github/hosted.js +0 -218
  396. package/dist/providers/github/rest.js +0 -321
  397. package/dist/providers/infisical/index.d.ts +0 -7
  398. package/dist/providers/infisical/index.js +0 -713
  399. package/dist/providers/infisical/skill.generated.js +0 -8
  400. package/dist/providers/intercom/index.d.ts +0 -24
  401. package/dist/providers/intercom/index.js +0 -121
  402. package/dist/providers/intercom/skill.generated.js +0 -8
  403. package/dist/providers/linear/index.d.ts +0 -55
  404. package/dist/providers/linear/index.js +0 -537
  405. package/dist/providers/linear/skill.generated.js +0 -8
  406. package/dist/providers/mixpanel/index.d.ts +0 -59
  407. package/dist/providers/mixpanel/index.js +0 -472
  408. package/dist/providers/mixpanel/operations.js +0 -20
  409. package/dist/providers/mixpanel/rest.js +0 -603
  410. package/dist/providers/mixpanel/skill.generated.js +0 -10
  411. package/dist/providers/mixpanel/value-safety.js +0 -16
  412. package/dist/providers/notion/index.d.ts +0 -67
  413. package/dist/providers/notion/index.js +0 -368
  414. package/dist/providers/notion/openapi.generated.js +0 -75
  415. package/dist/providers/notion/rest.d.ts +0 -24
  416. package/dist/providers/notion/rest.js +0 -1226
  417. package/dist/providers/notion/skill.generated.js +0 -11
  418. package/dist/providers/notion/value-safety.js +0 -56
  419. package/dist/providers/revenuecat/index.d.ts +0 -51
  420. package/dist/providers/revenuecat/index.js +0 -697
  421. package/dist/providers/revenuecat/skill.generated.js +0 -11
  422. package/dist/providers/stripe/rest.d.ts +0 -14
  423. package/dist/providers/stripe/rest.js +0 -402
  424. package/dist/providers/vercel/rest.d.ts +0 -20
  425. package/dist/providers/vercel/rest.js +0 -1020
  426. package/dist/providers/wispr-flow/index.d.ts +0 -14
  427. package/dist/providers/wispr-flow/index.js +0 -117
  428. package/dist/providers/wispr-flow/skill.generated.js +0 -8
  429. /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
+ credentialOwner: "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
 
@@ -1211,6 +1460,38 @@ a dispatched record. The refresh HTTP request and response body have a
1211
1460
  backoff. A waiter deadline returns retryable `unavailable`, keeps the grant,
1212
1461
  and starts no consent. It never permits resending a spent token.
1213
1462
 
1463
+ A `remoteMcp()` write the downstream answered with HTTP 401 is never sent
1464
+ again (INV-9). The SDK still refreshes the grant, and connecta stops its
1465
+ resend before it leaves, so any fresh call after the target is reconciled uses
1466
+ the fresh token. The call
1467
+ fails with a non-retryable `connector_call_failed` whose outcome is unknown.
1468
+ If the refresh fails instead, whatever it met (a dead grant, a waiter
1469
+ deadline, the caller leaving), or hands back the token the API just
1470
+ rejected, the 401's verdict stays:
1471
+ `downstream_oauth_required`, never retryable, with `reconciliationRequired`
1472
+ and `writeOutcome: "unknown"`, and an `authorize_connector` handoff that says
1473
+ to re-read the target before any fresh call. A read is refreshed and sent
1474
+ once more. MCP sends a tool only once when either its effective classification
1475
+ or its original server hints say write. A deployment write-to-read override
1476
+ cannot make a server-declared write replayable. Its failure after possible
1477
+ dispatch is also an unknown outcome, non-retryable and without wait advice;
1478
+ a proven unsent failure retains its own recovery advice. A direct call outside an
1479
+ invocation uses the dispatched definition, a reviewed write verdict, or the
1480
+ tool's hints, and fails closed as a write.
1481
+
1482
+ A remote MCP tool call that loses its internal dispatch record is refused
1483
+ before sending unless active calls for that tool prove it is a read. A
1484
+ write or an unproven call fails non-retryable with `writeOutcome: "unknown"`:
1485
+ tracking loss cannot establish whether an earlier message already sent it.
1486
+
1487
+ Google Workspace also uses the invocation's served classification
1488
+ before considering the HTTP method. A write-classified GET is never replayed
1489
+ after a 401; a read-classified POST may replay once. Without a served verdict,
1490
+ this transport uses GET as read and other methods as write. Google drops the
1491
+ rejected token for the next deliberate call and retains `auth_required` with
1492
+ `writeOutcome: "unknown"` and the operator handoff. A renewed credential does not establish the write outcome. Recovery
1493
+ that hands back the same token renewed nothing.
1494
+
1214
1495
  One preparation deadline covers initial epoch and grant reads, claim acquisition,
1215
1496
  dispatch preparation, and local joiners waiting for a grant commit. Cancellation
1216
1497
  before sending settles the local flight without awaiting cleanup storage. An
@@ -1287,7 +1568,29 @@ contain `key`, `token`, `secret`, `auth`, `signature`, or `session`. Custom
1287
1568
  `api()` handlers use `ctx.fetch` for this tracking; `ctx.oauth.fetch` and
1288
1569
  maintained-provider transports track the final request too. Credential-slot
1289
1570
  reads cover values sent in headers, queries, or bodies. Echoed sensitive
1290
- header lines are also withheld.
1571
+ header lines are also withheld, read before any echoed value in them is
1572
+ replaced: a private value that changes part of a header's name or takes a
1573
+ credential prefix (such as `Authorization: Bearer`) still leaves the whole
1574
+ header value withheld, every cookie pair included. A line that is not a
1575
+ sensitive header keeps its text.
1576
+
1577
+ Redaction removes every registered form whole and every run of eight
1578
+ consecutive code units (K) of a registered form of a value at least eight units
1579
+ long, wherever it starts, in the raw text and in each JSON-decoded layer. A cut,
1580
+ trim, or page can therefore leave at most seven units of a form outside the
1581
+ cases below. Text Connecta or a maintained provider shortens before returning
1582
+ it, such as a long error message, a capped field, or one page of a long text, is
1583
+ redacted whole before any trimming, whitespace normalization, number parsing,
1584
+ extraction, or cut, so derived titles, previews, summaries, sizes, and raw
1585
+ branches share the same protection. An extraction whose source changed under
1586
+ redaction returns `[redacted]`. Text read only up to a bound also loses a tail
1587
+ that begins a sent value the read stopped inside, at every JSON decoding layer.
1588
+ Complete matches and unfinished tails are found against the original text and
1589
+ replaced together; even a one-unit prefix is withheld. This can conservatively
1590
+ suppress a legitimate tail that shares a secret's prefix. Text paging that
1591
+ counts redacted units binds its cursor to that representation and refuses
1592
+ continuation if a changed request secret set changes it, even when the vendor
1593
+ revision is unchanged.
1291
1594
 
1292
1595
  Submitted values under JSON Schema `writeOnly: true` join this memory-only
1293
1596
  request set, including nested objects, arrays, and local references. Strings
@@ -1304,69 +1607,200 @@ submitted data and cached catalogs never depend on one request's arguments.
1304
1607
 
1305
1608
  Payload rules apply where downstream or guest data enters: downstream results
1306
1609
  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
1610
+ program returns, logs, and emits, and relayed input prompts. Each redaction
1611
+ projects the whole value: string and number leaves in serialization order,
1612
+ JSON objects, arrays, and string literals parsed into their own leaves whether
1613
+ they are wire text or a string inside a result, and errors' own fields. The
1614
+ joined projection is matched once, so a value split across adjacent leaves,
1615
+ such as text blocks or array items, is found; each match is replaced only in
1616
+ the leaves it touches, and the containers around them keep their shape. A
1617
+ number that a match touches becomes the string `[redacted]`, so the JSON stays
1618
+ valid. Text is reserialized only when something changed; otherwise its original
1619
+ bytes remain. Serialized JSON whose keys, syntax, and leaves together hold a run
1620
+ that no single leaf holds, as a value spanning tokens does, is withheld whole.
1621
+ Matching runs on the raw text and through one more layer of JSON string escapes
1622
+ at a time while the last layer still decodes an escape, at most eight layers;
1623
+ text still holding escapes past that is withheld. Each decoded layer is scanned
1624
+ around the escapes it decoded, since every other match was found a layer up. Every match is mapped back to
1625
+ the original text, overlapping spans merge, and each merged span is replaced
1626
+ once, so one match can never split another. A span wholly inside an existing
1322
1627
  `[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.
1628
+ placeholder text is still redacted.
1629
+
1630
+ A maintained provider's parsed vendor JSON has every value redacted on arrival
1631
+ and keeps its object keys until the connector returns, so the provider's value
1632
+ safety finds each field by the name the vendor gave it. The connector boundary
1633
+ then applies payload rules to the whole result or failure, keys included.
1634
+ Response header names are kept; their values are redacted. When a redacted
1635
+ value is one value safety decides on, such as an object's type, a record's
1636
+ label, a link's parameter name or the response's Content-Type, value safety
1637
+ takes its most protective choice, so more of that response may be withheld.
1638
+
1639
+ Every string that can reach an agent, an executor, a page or storage goes
1640
+ through one checker that applies all of the checks in this section: text and
1641
+ number leaves, object keys, declared binary fields, the bytes of typed binary,
1642
+ cursor claims, error fields, provider text lanes, bounded reads, and structural
1643
+ fields that are refused instead of rewritten. Object keys are checked on their
1644
+ own, and a key holding a match is renamed. Image, audio, resource-blob, or
1645
+ `format: "base64"` content, or a REST body's `base64` beside its `contentType`,
1646
+ is checked as text and also decoded whole and checked as bytes and as UTF-8,
1647
+ then withheld whole as the base64 of `[redacted]` on a match; content that is
1648
+ not valid base64 as it stands is still checked through every text view. Base64
1649
+ or base64url in any string is decoded and checked in each of the string's text
1650
+ views: the text itself, the text with one layer of JSON escapes decoded
1651
+ (`\uXXXX` and the standard escapes), the text with its percent escapes
1652
+ decoded, and its form decoding, which reads a `+` as a space and decodes any
1653
+ percent escapes, whether or not the text holds one. So `x+bm90ZXM=` is read
1654
+ both as one run and as `x` followed by `bm90ZXM=`, the base64 of `notes`.
1655
+ A view that is wholly base64 is decoded from six characters. A run inside a
1656
+ view is decoded from six characters when a private value of four to seven
1657
+ characters is registered, and from eleven otherwise: each length is the
1658
+ shortest run that can carry the smallest value the pass matches, four bytes or
1659
+ eight, at any alignment, padded or not. Decoded bytes are checked as bytes, as
1660
+ UTF-8, and through base64 they carry, up to three encodings deep, and the field
1661
+ is withheld whole as `[redacted]` when only its decoding holds a match. Every
1662
+ decode is paid for from the call's work budget; a field the budget cannot
1663
+ check is withheld.
1664
+
1665
+ Only fixed words are copied unchanged whatever was sent: Connecta, MCP, and
1666
+ JSON-RPC envelope member names, tag values such as a content `type` or result
1667
+ `format`, booleans under outcome names (`isError`, `ok`, `retryable`,
1668
+ `truncated`, `hasMore`), and, on an error, its classification code and a fixed
1669
+ name: one a built-in error, `DOMException` (`DOMException`, `AbortError`,
1670
+ `TimeoutError`), Connecta's own classes, or the pinned MCP SDK's classes give
1671
+ their instances. Any other error `name`, own or inherited, a data field or an
1672
+ accessor's answer, including one a custom class gives itself, is a leaf of the
1673
+ projection like any other field: it is matched joined with the fields beside it
1674
+ and through any JSON it holds, and replaced whole by `[redacted]` when anything
1675
+ changes any part of it. The copy keeps its class, so classification and
1676
+ identity facts still see it. An error's fields are joined in its copy's order:
1677
+ its own fields, then what its class answers for `stack`, `message`, `cause`,
1678
+ `code` and `name`, then the other accessors its class defines. A number or a string under any name,
1679
+ such as `retryAfterMs` or a plain object's `code`, is matched like any other
1680
+ value; a matching typed number on an error is dropped. Relay states and connect
1681
+ links Connecta minted are kept whole. The agent boundary around Connecta's own
1682
+ envelopes repeats every check except short private values, so envelope
1683
+ identifiers, counts, and JSON-RPC ids are never rewritten.
1684
+
1685
+ Every base64 or base64url encoding Connecta itself returns, such as a Drive or
1686
+ Gmail download, a REST body's `base64`, or a page cursor, is produced by one
1687
+ helper that checks the content first and encodes `[redacted]` instead when it
1688
+ holds a match. A cursor holds Connecta's own names, counts and fingerprints
1689
+ beside vendor values redacted when they were placed: a vendor page token or
1690
+ page id is redacted, so one that echoes a sent value fails on the next page
1691
+ instead of carrying it; a vendor revision or id the cursor only compares is
1692
+ replaced by its digest when it holds a sent value. Before a cursor is encoded,
1693
+ every string in it is checked under payload rules, and its whole text and
1694
+ encoding under the agent boundary's, which leave Connecta's own keys and counts
1695
+ to short private values. A cursor that holds a match encodes `[redacted]`,
1696
+ which the next page refuses.
1697
+
1698
+ What redaction returns is what it checked, and only JSON data: strings,
1699
+ numbers, booleans, `null`, plain objects and arrays, a fresh built-in
1700
+ `Uint8Array`, and copies of errors. A caller's object or array is
1701
+ returned as it stands only as verified plain data: its prototype is
1702
+ `Object.prototype`, `Array.prototype`, or `null`; no `toJSON` is on its chain;
1703
+ it has no symbol-keyed field; every field is a data property the pass read, an
1704
+ array's holes read through its prototypes; and nothing in it changed. Anything
1705
+ else is returned as a copy of writable, configurable data properties, so a
1706
+ frozen or read-only input never makes redaction throw, and the caller's value is
1707
+ never mutated:
1708
+
1709
+ - An error is always copied, as its own class. Its stack, and every accessor its
1710
+ class defines, such as DOMException's `name` and `message` or an SDK error's
1711
+ `status`, is read once into a checked field, under its name checked as any key
1712
+ is. Its `toJSON`, a data field or an
1713
+ accessor, is never read and is shadowed by an `undefined` field on the copy,
1714
+ and a field holding a function is withheld, so the copy carries no
1715
+ serializer. Methods its class defines stay on its prototype; JSON calls none
1716
+ of them.
1717
+ - An object's own accessor is never invoked and is left out of the copy; an
1718
+ array index read through an accessor holds `[redacted]`.
1719
+
1720
+ Every other value becomes the data JSON shows for it, checked, or is withheld:
1721
+
1722
+ | Value | Returned as |
1723
+ | ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------- |
1724
+ | Function | `[redacted]`; a `toJSON` it holds is never called |
1725
+ | Symbol | `[redacted]` |
1726
+ | `bigint` | itself, checked as its digits like a number; JSON exits refuse it, as before |
1727
+ | `undefined` | itself; JSON omits it |
1728
+ | 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` |
1729
+ | `String`, `Number`, or `Boolean` wrapper | its primitive, checked |
1730
+ | `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 |
1731
+ | Array of another class | a plain array of its indices, checked |
1732
+ | Typed array, `DataView`, or `ArrayBuffer`, including a subclass | a fresh built-in `Uint8Array` of the bytes it held, or `[redacted]` |
1733
+
1734
+ Typed binary is withheld as `[redacted]` when its bytes hold a match, checked as
1735
+ decoded binary content is, or when one of the numbers JSON shows for it does:
1736
+ its elements and, for the `Uint8Array` returned, its bytes, each checked as a
1737
+ JSON number and joined with its neighbours. A one-to-three-character numeric
1738
+ private value, such as `7`, therefore withholds a typed array holding that
1739
+ element, as it withholds an equal JSON number. Each of those numbers is an entry
1740
+ of the walk's 1,048,576-entry budget, so binary past it is withheld whole.
1741
+ Connecta returns downstream binary as base64 text, which none of this limits;
1742
+ only in-process code can return a typed array. With nothing registered for a
1743
+ request, redaction has nothing to match and returns its input as it stands.
1744
+
1745
+ A change copies only the containers on its path. An array's fields other than
1746
+ its indices are not read: JSON, which the MCP wire, the result stash, and every
1747
+ executor marshal values with, never serializes them.
1329
1748
 
1330
1749
  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.
1750
+ private value of one to three characters is matched only as a whole field
1751
+ value: a string, property name, number, boolean, or decoded binary view equal
1752
+ to it or a supported encoded form is withheld, and text around it stays, so a
1753
+ private `1` leaves `row 10` alone. A value of four to seven characters is also
1754
+ replaced wherever it occurs in payload text, numbers, property names, and
1755
+ decoded views, so `prod-db` becomes `[redacted]-db` for `prod`. Pieces of either
1756
+ are not matched. Private values of at least eight characters and credentials
1757
+ are matched whole and by every eight-unit run. At a cut, an unfinished prefix of
1758
+ any registered form is still withheld, however short.
1759
+
1760
+ Identifiers sent beside a credential are matched whole only, because consent
1761
+ URLs and vendor metadata carry their parts by design: a confidential OAuth
1762
+ client's ID, a Basic username beside a password of at least eight characters,
1763
+ and credential-slot fields named as identifiers (ending in `id`, `user`,
1764
+ `username`, `login`, or `email`, or starting with `public`). A Basic username
1765
+ beside a shorter or empty password is the credential itself and is fully
1766
+ matched. Query parameters named as continuation cursors (`pageToken`,
1767
+ `nextPageToken`, `next_token`, `continuationToken`, `syncToken`) are public
1768
+ result state and are not registered. Header values a connector adds to token
1769
+ requests are registered under any name, except values equal to Connecta's own
1770
+ public ones (`Accept: application/json`, and the media types maintained
1771
+ providers send): registering a media type would redact a vendor's name wherever
1772
+ its fragments recur.
1773
+
1774
+ The eight-unit runs are indexed as two independent 32-bit hashes per run, at
1775
+ most 512 Ki distinct runs, so a false match is vanishingly rare and only redacts
1776
+ more; repeated runs, as in a self-similar credential, are stored once. Indexing
1777
+ and scanning are linear in the registered forms and the scanned text, whatever
1778
+ the number of values. The `Bearer ` and `token ` framings are public, so no run
1779
+ starting inside them is indexed; an echo carrying a framing is replaced with it.
1780
+ Short private values and identifiers are matched whole by one Aho-Corasick
1781
+ automaton each, which also finds the unfinished tails of bounded reads. High-
1782
+ entropy credentials and private values do not match ordinary text. A value that
1783
+ embeds ordinary text, such as a password holding a word, a token holding a
1784
+ vendor's name, or a credential that is a URL, also redacts that text where it
1785
+ recurs within the same request. Submitted values register at most 512 KiB of
1786
+ encoded forms and 4,096 argument nodes. A value that does not fit either limit,
1787
+ or a set past the index's capacity, withholds every string of eight units or
1788
+ more and every run of adjacent shorter leaves reaching eight units; an argument
1789
+ tree that cannot be walked withholds all downstream text.
1358
1790
 
1359
1791
  One redaction call has one work budget across parsing and every scanned view:
1360
1792
  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.
1793
+ multi-MiB document and skill-file reads. When the joined projection does not
1794
+ fit, leaves are matched alone, in order, and one that does not fit is withheld
1795
+ without spending what a smaller one needs; runs crossing two kept leaves are
1796
+ then matched 512 units deep on each side of their seam. The same call visits at
1797
+ most 512 containers deep and 1 Mi entries in total. A container's entries are
1798
+ counted before it is walked.
1799
+ One that does not fit the remaining entries is withheld whole: it is never
1800
+ serialized and checked as text, so nothing past the budget reaches a result, a
1801
+ page, or the result stash. A cycle or a container past the depth limit is
1802
+ withheld. No limit throws or
1803
+ exposes a matcher diagnostic, and typed success outcomes remain.
1370
1804
 
1371
1805
  Only credentials of at least eight characters enter the redaction and structural
1372
1806
  matchers. Credentials shorter than 8 characters are not redacted from echoes;
@@ -1376,9 +1810,9 @@ this floor produces one operator warning per connector with the typed code
1376
1810
  `short_secret_not_redacted` and no credential value. Remote connectors warn at
1377
1811
  construction; static `api()` OAuth warns on its first provider operation.
1378
1812
  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
1813
+ The index and automata are cached until the secret set changes, and an empty set skips matching. Redaction
1814
+ runs before JSON unwrapping or joining text blocks, on final serialized text and every
1815
+ structured value, and before result paging, emits, and program outputs/errors/logs. See
1382
1816
  [the agent boundary](./architecture.md#errors-and-records).
1383
1817
 
1384
1818
  ## URLs a downstream advertises
@@ -1498,8 +1932,8 @@ createConnecta({
1498
1932
  auth: cloudflareAccessAuth(),
1499
1933
  // Administers shared credentials, agent tokens, and activity.
1500
1934
  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 } },
1935
+ // Each member connects their own account on personal_crm.
1936
+ pools: { team: { tools: ["shared_docs", "personal_crm", "notes.search_pages"], members: team } },
1503
1937
  connectors, executor,
1504
1938
  readOnly: false, // required: true refuses every write call
1505
1939
  });
@@ -1539,7 +1973,8 @@ Connecta configuration still decides connector access and management: `maintaine
1539
1973
  Protect the Worker with a Worker-level Access application whose destination is
1540
1974
  `{ "type": "worker", "worker_id": "<the Worker script tag>" }`. A traditional
1541
1975
  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/)
1976
+ the Worker. The Worker example's `alchemy.run.ts` declares this application and
1977
+ 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
1978
  on that Worker-level application for interactive MCP clients. Cloudflare then
1544
1979
  owns the unauthenticated challenge and `/.well-known/` metadata, issues opaque
1545
1980
  RFC 8707 tokens, and resolves them into the same trusted Worker identity.
@@ -1627,7 +2062,10 @@ claims. SDK 3.12.0 drops opaque `aud`, so connecta makes one additional Backend
1627
2062
  API verification request and reads the raw response. Its subject and client
1628
2063
  must agree with the SDK, and it must report an unrevoked, unexpired token.
1629
2064
  The extra call follows request cancellation, times out after ten seconds,
1630
- caches no token or audience, and denies admission on failure.
2065
+ reads at most 64 KiB, follows no redirect, caches no token or audience, and
2066
+ denies admission on failure, including a redirect. It ends its request and
2067
+ response body on every outcome, including an error status and a body that
2068
+ stops arriving.
1631
2069
 
1632
2070
  ### Explicit fallback for unbound tokens
1633
2071
 
@@ -1644,6 +2082,140 @@ for this fallback. Hosts may cache registrations, but the list is impractical
1644
2082
  for standard onboarding. A CIMD client identifies the host, not the deployment
1645
2083
  resource. Prefer resource audiences with `allowedOAuthClientIds` omitted or `[]`.
1646
2084
 
2085
+ ### One Clerk instance, one Organization per deployment
2086
+
2087
+ `organizationId` lets one Clerk instance serve several deployments. Give each
2088
+ deployment its own Clerk Organization and pass that Organization's id. In the
2089
+ Node template, set `CLERK_ORGANIZATION_ID` instead of editing source.
2090
+
2091
+ ```ts
2092
+ clerkAuth({ publishableKey, secretKey,
2093
+ publicUrl: "https://crm.example.com",
2094
+ organizationId: "org_..." });
2095
+ ```
2096
+
2097
+ Admission in organization mode:
2098
+
2099
+ - Membership is the only organization rule. Every admitted Clerk user is a
2100
+ member of that Organization. Connecta asks Clerk's Backend API,
2101
+ `GET /v1/organizations/{organization_id}/memberships?user_id={user_id}`,
2102
+ for MCP OAuth tokens (JWT and opaque) and for operator sessions. It caches
2103
+ each answer per Organization and user, counted from when the lookup
2104
+ started: a member for 60 seconds, a non-member for 30. Each request that
2105
+ misses the cache runs its own lookup, subject to the bounds below. An answer
2106
+ is cached only if no lookup that started later has already cached one, so an
2107
+ older answer never replaces a newer one. If eviction suppresses a newer
2108
+ answer, any older answer for that user is invalidated. Assuming the clock
2109
+ does not step back unseen within the TTL window described below, every
2110
+ request reaching the check 60 seconds or more after a member's removal in
2111
+ Clerk is refused, even while their day-long access token remains valid.
2112
+ Under the same clock assumption, a non-member answer expires within 30
2113
+ seconds of an addition; admission then requires a successful lookup.
2114
+ - The cache reads the deployment's clock. An answer is used only while the
2115
+ clock reads from its lookup's start to that start plus 60 or 30 seconds.
2116
+ A step forward only ends it early. A step back to before the start ends it
2117
+ too, and once an answer has been found expired no later step revives it. A
2118
+ step back that lands within that window before any request has seen the
2119
+ answer expire cannot be detected, and keeps the answer until the clock
2120
+ again reads its start plus the TTL. That unseen rollback can extend cached
2121
+ admission beyond 60 seconds of elapsed time after removal.
2122
+ - Organization claims are never read. A token's `org_id` and a session's
2123
+ active Organization neither admit nor refuse. A member's token is bound to
2124
+ this deployment's URL whichever Organization they chose in Clerk, and a
2125
+ non-member gets `403` from the lookup.
2126
+ - A lookup that fails, answers with a redirect, an error status other than
2127
+ `429` (handled below), or an unreadable or oversized body, or times out ten seconds after it started
2128
+ refuses its request with `403`. Redirects are never followed. A caller that
2129
+ leaves cancels its own lookup and is refused; other requests' lookups go
2130
+ on. Connecta ends the Backend API request and its body on every outcome and
2131
+ caches neither a failure nor a cancelled lookup, so the next request asks
2132
+ Clerk again, subject to the bounds and 429 backoff below.
2133
+ - Connecta checks the token's audience, issuer, and subject before the lookup.
2134
+ The issuer must be the Frontend API origin encoded in `publishableKey`, and
2135
+ the subject must be the user the lookup names. An opaque token's
2136
+ verification response comes from the instance's Backend API and is matched by
2137
+ subject instead. A mismatch gets `401`.
2138
+ - Organization roles and permissions grant nothing. `maintainers` and pool
2139
+ `members` still list Clerk user ids in code. A member of the Organization in
2140
+ neither list gets `404` at `/mcp` and at every `/mcp/<pool>`.
2141
+ - `allowedDomains` and `gate` still apply after membership. Every configured
2142
+ check must pass, and an outsider never reaches `gate`.
2143
+ - Resource-bound tokens are required. A nonempty `allowedOAuthClientIds`
2144
+ throws at construction; omit it or pass `[]`. Every deployment on a shared
2145
+ instance gets tokens from one issuer, so the resource audience is what keeps
2146
+ one deployment's token out of another.
2147
+ - Advertised scopes are unchanged. Organization mode needs no Clerk
2148
+ organization scope, operator page, or sign-in page setting.
2149
+
2150
+ Protect the shared Backend API quota:
2151
+
2152
+ - Put ingress rate limiting in front of every deployment. Budget the combined
2153
+ traffic of all deployments and other applications using the instance,
2154
+ including authenticated non-members and opaque-token authentication. For
2155
+ example, rate limit both `crm.example.com` and `tickets.example.com` within
2156
+ one shared quota budget when they use the same Clerk instance; independent
2157
+ limits that each consume the whole quota do not protect the other deployment.
2158
+ [Clerk's system limits](https://clerk.com/docs/guides/how-clerk-works/system-limits)
2159
+ apply per instance identified by the secret key: 1,000 Backend API requests
2160
+ per ten seconds in production, 100 in development. Membership reads use that
2161
+ general limit. A deployment's MCP concurrency setting does not bound all
2162
+ authentication paths, including `subscriptions/listen` and operator routes.
2163
+ The independent [authentication capacity bound](#authentication-capacity-and-ingress-limiting)
2164
+ covers those paths and the SDK's authentication calls before this adapter's
2165
+ organization-specific bounds; it still does not impose a rate budget.
2166
+ - Organization mode permits at most 16 concurrent membership/extra opaque
2167
+ verification calls per `clerkAuth` instance (per isolate on Workers), with at
2168
+ most two per user. Each call owns its signal, deadline, and response body;
2169
+ there is no shared promise or waiting queue. Excess calls immediately get
2170
+ HTTP `503`, fixed code `organization_lookup_busy`, `retryable: true`, and
2171
+ `Retry-After: 1`. These fixed caps limit bursts relative to the documented
2172
+ quotas; they do not limit sustained request rates or coordinate isolates,
2173
+ deployments, or the SDK's own authentication calls. Do not treat them as a
2174
+ substitute for ingress rate limiting.
2175
+ - A `429` from a membership read or extra opaque verification starts backoff
2176
+ shared by those calls in that isolate. Connecta honors `Retry-After` seconds
2177
+ (or an HTTP date), clamped to 1–60 seconds; missing or malformed values use
2178
+ ten seconds. Concurrent 429s can extend, never shorten, the current backoff.
2179
+ The triggering request and new misses during backoff receive the same
2180
+ retryable `503` and a `Retry-After` for the remaining whole seconds, rounded
2181
+ up. Backoff is timed on the wall clock, so a clock step back while it runs
2182
+ lengthens the window by the size of the step. No membership verdict is
2183
+ cached for these refusals. Valid cached
2184
+ membership answers remain usable; opaque tokens still need verification.
2185
+ After backoff expires the next miss asks Clerk again, with no automatic
2186
+ retry of the original request. Monitor the payload-free
2187
+ `organization_lookup_busy` log reason and adjust the ingress budget when it
2188
+ recurs. A quota exhausted by another deployment can still refuse uncached
2189
+ admission here.
2190
+
2191
+ Set up a shared instance:
2192
+
2193
+ 1. Enable Organizations on the Clerk instance. Create one Organization per
2194
+ deployment. Add each person who signs in to that deployment as a member;
2195
+ any role works.
2196
+ 2. Enable resource audiences as described in
2197
+ [Enable resource audiences in Clerk](#enable-resource-audiences-in-clerk).
2198
+ Each deployment has its own public URL, so its tokens name only it.
2199
+ 3. Set `organizationId` to the deployment's Organization id. List
2200
+ `maintainers` and pool `members` by Clerk user id as usual.
2201
+ 4. Restart or redeploy. Verify that a member is admitted at `/mcp` and on the
2202
+ operator pages, a signed-in non-member gets `403`, and a token issued for
2203
+ another deployment's URL gets `401`.
2204
+
2205
+ Isolation tradeoff: the secret key is instance-wide. It reaches every
2206
+ Organization, user, and OAuth application on the instance, so whoever reads it
2207
+ from one deployment's environment can read and change every other
2208
+ deployment's membership. Organization mode separates admission, not
2209
+ administration. When a deployment needs its own keys, users, or instance
2210
+ settings, give it its own Clerk instance and omit `organizationId`; that shape
2211
+ remains fully supported.
2212
+
2213
+ Organization denials log the fixed codes `organization_membership_denied`,
2214
+ `organization_lookup_failed`, and `token_identity_mismatch`. They carry no user
2215
+ ids, Organization ids, or Backend API response text.
2216
+
2217
+ ### Session and diagnostic boundaries
2218
+
1647
2219
  Clerk session tokens authenticate operator routes and browser OAuth callbacks
1648
2220
  only, with the deployment-origin `azp` check. MCP rejections retain the `401`
1649
2221
  Bearer challenge and endpoint `resource_metadata` URL required by
@@ -1679,7 +2251,9 @@ that is not `pk_test_`/`pk_live_` and a base64-encoded domain throws when
1679
2251
  `clerkAuth` is called, as `allowedDomains` does, naming the option without
1680
2252
  quoting the value — usually the _secret_ key pasted into the publishable slot.
1681
2253
  A per-request deployment, as on Workers, sees that error on its first request
1682
- instead of a base64 stack on every route.
2254
+ instead of a base64 stack on every route. `organizationId` must be `org_`
2255
+ followed by 27 letters or digits, and it cannot be combined with a nonempty
2256
+ `allowedOAuthClientIds`; either mistake throws at construction.
1683
2257
 
1684
2258
  ## Human authentication management
1685
2259
 
@@ -1739,6 +2313,25 @@ and Origin checks. Vaults without sealing keep ordinary handoffs and cannot
1739
2313
  serve URL elicitation. See [authorization recovery](./meta-tools.md#authorization-recovery)
1740
2314
  for capability gating, retry termination, and dispatch restrictions.
1741
2315
 
2316
+ Downstream OAuth credential POSTs (code exchange, refresh, dynamic client
2317
+ registration and revocation) use a transport that sends each request at most
2318
+ once and never follows redirects. On Node, serve with `listen` from
2319
+ `@zackbart/connecta/node` and retain the `undici` peer; custom root-only Node
2320
+ hosts refuse these POSTs before dispatch. Node's explicit `listen({ proxy })`
2321
+ option applies to them. Discovery GETs keep native fetch and its host proxy
2322
+ policy. Workers use their native fetch transport. See [Node transport setup](./deploying.md#start-on-node).
2323
+
2324
+ A refresh without a confirmed usable answer may have rotated the refresh token.
2325
+ Connecta blocks its fingerprint and requires a fresh authorization flow, without
2326
+ replaying that token or claiming the server did not receive it. Authorization
2327
+ can still be restarted; an unresolved fingerprint cannot become redeemable again
2328
+ by writing the same token into an old grant. Registration failure can leave a
2329
+ client at the provider; check the provider's client administration before
2330
+ starting another registration if its reply was lost. Disconnect removes the
2331
+ local grant before attempting revocation, and an unconfirmed revocation asks the
2332
+ operator to revoke it at the provider. No token forms or provider error prose
2333
+ are included in diagnostics.
2334
+
1742
2335
  A verified `/connect` visit gives downstream OAuth work and its state handoff 30 seconds. The request signal
1743
2336
  and deadline reach discovery, registration, and other downstream fetches; expiry returns `504 OAuth
1744
2337
  authorization start timed out`. A restart's grant write, catalog invalidation, and scope close drain before
@@ -1759,9 +2352,15 @@ its URL.
1759
2352
 
1760
2353
  Credential and OAuth mutation require interactive identity, connector visibility, management permission, and
1761
2354
  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.
2355
+ visibility only. With `ui: operatorUi()` and a vault, a missing credential returns a secret-free
2356
+ `operator_config` handoff; without either it returns `unavailable`. When all declared credential
2357
+ fields are configured, the explicit result returns `ready` with conditional guidance and retains
2358
+ the operator handoff fields (or the deployment configuration message when recovery is unavailable).
2359
+ If a call just failed with an authentication or operator-configuration error, give that handoff
2360
+ to the operator; otherwise proceed with the call. Presence does not validate a key, and this
2361
+ stateless result cannot remember failures from earlier requests. Authentication errors retain
2362
+ `oauth`, `operator_config` or `unavailable` recovery, never `ready`. The OAuth branch also checks
2363
+ management permission and returns `unavailable` when refused.
1765
2364
 
1766
2365
  Core owns `/connect/<connector>` and the callback without the optional UI. Both require Clerk sessions or
1767
2366
  Cloudflare Access users; MCP OAuth tokens, `cta_` tokens, and Access service identities
@@ -1844,9 +2443,9 @@ path, without credentials or a fragment. Its `client_id` must equal its URL,
1844
2443
  and its metadata must include the deployment's exact callback and public-client
1845
2444
  authentication. Changing from an external document to the self-hosted URL needs
1846
2445
  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.
2446
+ Basecamp restricts DCR for HTTPS callbacks, so its generic `remoteMcp()` entry
2447
+ needs a public HTTPS deployment or an external document. Preserve its client
2448
+ identity when following the [hosted MCP migration guide](./hosted-mcp-migration.md).
1850
2449
 
1851
2450
  For a pre-registered client, use:
1852
2451
 
@@ -1924,10 +2523,28 @@ owner's access token as `Authorization: Bearer` with these rules:
1924
2523
  untrusted response URLs. Return redirects unfollowed; handlers cannot set `Authorization`.
1925
2524
  - On 401, recover exactly once using an already rotated token or one coalesced
1926
2525
  refresh through `remoteMcp()`'s coordinator. Persist rotation even when its
1927
- owner is cancelled after the answer. Replay once; stream bodies are refused.
2526
+ owner is cancelled after the answer. Only a positively read-classified
2527
+ GET/HEAD without method-override headers replays once;
2528
+ stream bodies are refused.
2529
+ - A request the core classifies as a write never replays (INV-9). Positive read
2530
+ classification comes from deployment `classification` first and otherwise the rule the
2531
+ registry applies to the tool's hints (a contradictory `readOnlyHint: true`
2532
+ with `destructiveHint: true` is a write). The actual method must also be GET
2533
+ or HEAD; an override header, missing read evidence, or a POST remains a
2534
+ one-attempt write, even when the tool calls it a read. After the recovery,
2535
+ the call fails with a non-retryable `connector_call_failed` whose outcome is
2536
+ unknown, and the renewed grant serves any fresh call the agent makes after
2537
+ reconciling the target.
2538
+ - Whatever that recovery meets, a write's failure is never retryable and its
2539
+ outcome is unknown. Until a refresh succeeds the 401's verdict stands:
2540
+ `downstream_oauth_required`, which the invocation marks
2541
+ `reconciliationRequired`, with the `authorize_connector` handoff, whether
2542
+ the refresh was [dead](#refresh-failures), lost after it was sent, raced
2543
+ by a disconnect or reset, failed in storage or contention, or handed back
2544
+ the token the API just rejected, which renews nothing.
1928
2545
  - No grant, a second 401, or a [dead refresh](#refresh-failures) means
1929
2546
  `downstream_oauth_required`, directing `authorize_connector`. Refresh waiter deadlines
1930
- return retryable `unavailable`. Latch second 401s for the request scope,
2547
+ return retryable `unavailable` for a read. Latch second 401s for the request scope,
1931
2548
  preventing repeated refreshes by later program calls.
1932
2549
  - Handlers can name no storage, sealing, or owner partition; the registry owns them.
1933
2550
  - The answer's `.text()` and `.json()`, clones' too, decode its bytes as UTF-8, so
@@ -1963,6 +2580,90 @@ provider skill explains App permissions and installation prerequisites.
1963
2580
 
1964
2581
  ## Contributor reference
1965
2582
 
2583
+ ### INV-5 output redaction limits
2584
+
2585
+ The output boundary's guarantee is K = 8 units, and these are its exact limits:
2586
+
2587
+ - A run of eight consecutive UTF-16 units of a registered form is matched
2588
+ wherever it appears, raw, under JSON escapes, or in a decoded base64 view.
2589
+ - Pieces shorter than eight units are matched only where they are adjacent:
2590
+ the projection joins neighbouring leaves (array items, text blocks), and
2591
+ provider lanes join one source's runs (one Slides shape's text runs, one Docs document's text). Pieces shorter than eight
2592
+ units that are not adjacent, such as the two seven-unit halves of a fourteen-
2593
+ unit value in two Slides shapes with other leaves between them, survive: they
2594
+ are indistinguishable from ordinary text without redacting ordinary words.
2595
+ - Object keys are checked on their own and never join the value projection, so
2596
+ a key never separates two adjacent values. A value split between a key and
2597
+ its value with each piece shorter than eight units, such as a seven-unit key
2598
+ holding a seven-unit value, survives like any other non-adjacent pieces; a
2599
+ piece of eight or more units is still matched in either place.
2600
+ - Private values of one to three units match whole field values only; four to
2601
+ seven also as substrings; eight and more also by every eight-unit run.
2602
+ - One checker (`checked()` in `src/sent-secrets.ts`) applies everything below
2603
+ to every output-bound string: leaves, keys, scalars, declared binary, typed
2604
+ binary, cursor claims and Connecta's own encodings, provider lanes, bounded
2605
+ reads and structural refusals. Its matching steps run only inside it; a
2606
+ serialized container's cross-token scan, which can only withhold it whole, is
2607
+ the one other reader of the fragment index. The test "there is one string
2608
+ checker" spies on every step and fails when one runs outside it.
2609
+ - A Proxy, which only in-process code can construct, can answer a later read
2610
+ differently from the one redaction made; the guarantee covers what redaction
2611
+ read. So can code on an error class's prototype, which its copy keeps so that
2612
+ classification still sees its class; JSON calls none of it once `toJSON` is
2613
+ shadowed. Only verified plain data is returned as it stands; everything else
2614
+ is a copy of checked data properties, and every value is JSON data, a fresh
2615
+ `Uint8Array`, or an error copy.
2616
+ - An array's fields other than its indices are never read, because JSON never
2617
+ serializes them.
2618
+
2619
+ The encoding lattice is exactly this, and anything outside it is out of scope:
2620
+
2621
+ - Text views of every string, whether a leaf, a key, a declared binary field or
2622
+ a cursor claim: the raw text; one layer of JSON escapes decoded
2623
+ (`\uXXXX` and `\" \\ \/ \b \f \n \r \t`); percent escapes decoded; and
2624
+ the form decoding, a `+` read as a space with any percent escapes decoded,
2625
+ generated whenever a `+` appears. Text matching itself also reads
2626
+ through up to eight JSON escape layers and matches registered percent forms.
2627
+ - In every text view, base64 and base64url runs are decoded. A run is a maximal
2628
+ span of `A-Z a-z 0-9 + / - _`, decoded from its first unit; up to two `=`
2629
+ after it are padding and are never counted. A whole view is decoded from six
2630
+ units; a run inside one from `SHORT_RUN_UNITS` = 6 under payload rules with a
2631
+ four-to-seven-unit private value registered, otherwise `FRAGMENT_RUN_UNITS` = 11.
2632
+ - Derivation: a run of L units decodes to floor(3L/4) bytes, so p bytes that start
2633
+ a bytes into the encoding (alignment a = 0, 1 or 2) need L >= ceil(4(a + p)/3),
2634
+ least at a = 0. The smallest piece a payload pass matches is a short value,
2635
+ four units and so at least four bytes: ceil(16/3) = 6. Otherwise it is K = 8
2636
+ units, at least eight bytes: ceil(32/3) = 11. Padding follows the run, so a
2637
+ padded and an unpadded encoding decode alike. The tests cover four-, seven-
2638
+ and eight-unit values at every alignment, padded and unpadded, in both
2639
+ alphabets.
2640
+ - Decoded bytes are matched as bytes, as UTF-8 when a form is not ASCII, and
2641
+ through their own JSON escapes within the eight-layer limit. Every decode is
2642
+ charged to the call's work budget, and content the budget cannot check is
2643
+ withheld.
2644
+ - Bytes, decoded or held by a typed array, are checked as bytes, through their
2645
+ JSON escapes, as UTF-8, and through the base64 they carry. Text views are
2646
+ views of strings: bytes that need a percent or form decoding before their
2647
+ base64 shows are two decodes chained, outside the lattice.
2648
+ - Out of scope: two or more text-layer decodes chained (percent escapes inside
2649
+ JSON escapes, or two JSON layers before base64); base64 of base64 (decoded
2650
+ content is searched for base64 up to three levels deep, but the guarantee does
2651
+ not depend on it); a base64 encoding glued to preceding alphabet units with no
2652
+ separator, such as a path segment after `/`, which decodes misaligned; HTML
2653
+ entities; compression; custom alphabets; encryption; bytes written as a
2654
+ JSON array of numbers; and one encoding split across separate leaves, such as
2655
+ the base64 of a value cut into array items. Raw text and JSON escapes are
2656
+ matched across adjacent leaves, but base64 and base64url decoding operates
2657
+ within each string.
2658
+ - The boundary removes echoes of values Connecta sent. A downstream already
2659
+ holds what it was sent and can always return it in a form outside this
2660
+ lattice, so redaction is not a defense against a downstream that reshapes a
2661
+ value on purpose.
2662
+
2663
+ `test/sent-secret-output-boundary.test.ts` holds the evidence, including its
2664
+ `review round 3`, `review round 4` and `review round 5` blocks for these limits;
2665
+ `test/writeonly-wire-redaction.node.test.ts` runs the QuickJS guest cases.
2666
+
1966
2667
  ### Source and tests
1967
2668
 
1968
2669
  [Decision 0007](https://github.com/zackbart/connecta/blob/main/decisions/0007-agent-pool-layout.md)
@@ -1973,6 +2674,8 @@ boundaries, and `src/storage/keys.ts` owns credential key families.
1973
2674
  Inbound adapters live in [src/auth](https://github.com/zackbart/connecta/tree/main/src/auth);
1974
2675
  [auth routes](https://github.com/zackbart/connecta/blob/main/src/routes/oauth.ts)
1975
2676
  own connection handoffs and callbacks. Check [inbound auth tests](https://github.com/zackbart/connecta/blob/main/test/inbound-auth.test.ts),
2677
+ [Clerk organization tests](https://github.com/zackbart/connecta/blob/main/test/clerk-organization.test.ts)
2678
+ and their [workerd counterpart](https://github.com/zackbart/connecta/blob/main/test/clerk-organization-workerd.test.ts),
1976
2679
  [Access tests](https://github.com/zackbart/connecta/blob/main/test/cloudflare-access-auth.test.ts),
1977
2680
  and [identity-scope tests](https://github.com/zackbart/connecta/blob/main/test/identity-scope.test.ts)
1978
2681
  for credential precedence and disclosure boundaries.
@@ -1985,3 +2688,23 @@ Downstream OAuth implementation and evidence are in
1985
2688
  [refresh leases](https://github.com/zackbart/connecta/blob/main/test/oauth-refresh-lease.test.ts),
1986
2689
  and [API OAuth tests](https://github.com/zackbart/connecta/blob/main/test/api-oauth.test.ts).
1987
2690
  Use the test pointers within each section for narrower failure cases.
2691
+
2692
+ ### Authentication execution (contributor)
2693
+
2694
+ Built-in authentication and downstream OAuth use private native programs behind
2695
+ the same Promise adapter and SDK contracts. Recognition still selects one
2696
+ verifier, and a refusal never falls back to another identity provider. Native
2697
+ operations use the existing request owner; cancellation cannot make a closed
2698
+ caller consume a refresh result or send another resource request. Once the
2699
+ claim CAS is submitted, the bounded refresh coordinator completes independently
2700
+ and commits a successful rotation under the existing epoch and spent-token
2701
+ rules. Code exchange does not acquire that coordinator ownership.
2702
+
2703
+ Authentication capacity remains charged until the underlying uncancellable work
2704
+ settles or collection proves that work unreachable. Abandonment alone never
2705
+ releases capacity. A native
2706
+ refactor does not change membership cache TTLs, audience/resource bindings,
2707
+ agent namespaces, credential partitions, OAuth transport policy or the rule of
2708
+ one physical attempt per credential-bearing POST. See the
2709
+ [native auth implementation](./architecture.md#native-auth-programs-contributor)
2710
+ for contributor boundaries and retained Promise leaves.