@zackbart/connecta 0.37.1 → 0.38.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 (168) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +2 -2
  3. package/bin/connecta.mjs +1 -39
  4. package/bin/version.mjs +1 -1
  5. package/dist/access-tokens.js +2 -4
  6. package/dist/agent-identity.js +1 -1
  7. package/dist/auth/clerk-sdk/generated.js +2 -1
  8. package/dist/auth/downstream-oauth.js +19 -153
  9. package/dist/auth/downstream-revocation.js +1 -1
  10. package/dist/auth/oauth-grant.js +11 -0
  11. package/dist/auth/oauth-token-response.js +1 -1
  12. package/dist/auth/vercel-agent.js +2 -1
  13. package/dist/auth-elicitation.js +2 -2
  14. package/dist/branding.d.ts +1 -15
  15. package/dist/branding.js +2 -2
  16. package/dist/bytes.js +42 -0
  17. package/dist/call-admission-context.js +2 -5
  18. package/dist/call-recovery.js +0 -3
  19. package/dist/catalog-fingerprint.js +2 -18
  20. package/dist/catalog-service.js +1 -1
  21. package/dist/config-defaults.d.ts +1 -0
  22. package/dist/config-defaults.js +1 -0
  23. package/dist/config-schema.d.ts +1 -1
  24. package/dist/config-schema.js +6 -2
  25. package/dist/config.d.ts +41 -14
  26. package/dist/config.js +21 -71
  27. package/dist/connectors/api-connector.js +4 -3
  28. package/dist/connectors/guarded-fetch.d.ts +0 -6
  29. package/dist/connectors/guarded-fetch.js +1 -11
  30. package/dist/connectors/remote-mcp.js +10 -43
  31. package/dist/connectors/resource-uri.js +0 -4
  32. package/dist/d1.d.ts +0 -89
  33. package/dist/d1.js +0 -34
  34. package/dist/downstream-input.js +2 -2
  35. package/dist/execute.js +22 -13
  36. package/dist/executor-result.js +2 -1
  37. package/dist/executors/quickjs-runtime.js +2 -25
  38. package/dist/executors/quickjs.js +1 -1
  39. package/dist/guest-runtime.js +5 -3
  40. package/dist/guest-types.d.ts +5 -5
  41. package/dist/http/client.js +1 -1
  42. package/dist/http/errors.js +2 -2
  43. package/dist/http/json-schema.js +1 -1
  44. package/dist/http/node-transport.js +0 -1
  45. package/dist/http/oauth.js +1 -1
  46. package/dist/http/paging.js +6 -9
  47. package/dist/http/provider.js +1 -1
  48. package/dist/http/safety.js +1 -1
  49. package/dist/http/services.js +1 -8
  50. package/dist/http/tools.js +1 -7
  51. package/dist/http/vendor.js +7 -7
  52. package/dist/identity.js +2 -6
  53. package/dist/invocation-auth.js +2 -1
  54. package/dist/meta-tools.js +27 -27
  55. package/dist/native-tools.js +2 -1
  56. package/dist/oauth-handoff.js +2 -2
  57. package/dist/operation-vocabulary.js +1 -1
  58. package/dist/operator-record.js +1 -49
  59. package/dist/operator-ui/contract.d.ts +2 -2
  60. package/dist/operator-ui/generated.js +3 -3
  61. package/dist/program-writes.js +3 -8
  62. package/dist/provider-call.js +0 -7
  63. package/dist/provider.d.ts +1 -1
  64. package/dist/providers/_shared/google/kit.js +1 -6
  65. package/dist/providers/_shared/google/service-account.js +1 -3
  66. package/dist/providers/_shared/google/workspace.d.ts +1 -1
  67. package/dist/providers/_shared/guide.js +3 -0
  68. package/dist/providers/_shared/json.js +12 -0
  69. package/dist/providers/_shared/rest/compact-index.js +1 -1
  70. package/dist/providers/_shared/rest/kit.js +1 -3
  71. package/dist/providers/_shared/rest/operation-index.js +1 -3
  72. package/dist/providers/_shared/rest/value-safety.js +26 -28
  73. package/dist/providers/breeze/api.js +2 -7
  74. package/dist/providers/breeze/index.js +4 -2
  75. package/dist/providers/breeze/tools.js +3 -4
  76. package/dist/providers/ccb/api.js +1 -6
  77. package/dist/providers/ccb/index.js +2 -1
  78. package/dist/providers/ccb/tools.js +1 -6
  79. package/dist/providers/cloudflare/api.js +2 -2
  80. package/dist/providers/cloudflare/index.js +2 -1
  81. package/dist/providers/cloudflare/safety.js +3 -3
  82. package/dist/providers/docs/tools.js +2 -10
  83. package/dist/providers/drive/tools.js +12 -25
  84. package/dist/providers/forms/tools.js +1 -9
  85. package/dist/providers/gmail/tools.js +5 -21
  86. package/dist/providers/overflow/api.js +1 -3
  87. package/dist/providers/overflow/index.js +2 -1
  88. package/dist/providers/overflow/tools.js +3 -7
  89. package/dist/providers/planning-center/api.js +2 -1
  90. package/dist/providers/planning-center/lib/constants.d.ts +1 -1
  91. package/dist/providers/planning-center/lib/json-api.js +1 -9
  92. package/dist/providers/planning-center/tools.js +2 -1
  93. package/dist/providers/sheets/tools.js +1 -9
  94. package/dist/providers/slides/tools.js +4 -13
  95. package/dist/providers/stripe/index.js +2 -1
  96. package/dist/providers/stripe/safety.js +2 -2
  97. package/dist/providers/tithely/api.js +2 -7
  98. package/dist/providers/tithely/index.js +3 -2
  99. package/dist/providers/tithely/tools.js +7 -5
  100. package/dist/providers/vercel/api.js +1 -6
  101. package/dist/providers/vercel/lib/pin.js +1 -1
  102. package/dist/providers/vercel/safety.js +1 -1
  103. package/dist/providers/vercel/tools.js +2 -1
  104. package/dist/redaction-marker.js +2 -2
  105. package/dist/registry.d.ts +1 -1
  106. package/dist/registry.js +5 -7
  107. package/dist/request-principal.js +1 -1
  108. package/dist/request-state.js +2 -2
  109. package/dist/routes/access-tokens.js +1 -1
  110. package/dist/routes/activity-disclosure.js +1 -1
  111. package/dist/routes/connect.js +2 -2
  112. package/dist/routes/credentials.js +1 -1
  113. package/dist/routes/mcp.js +4 -3
  114. package/dist/routes/oauth-management.js +1 -1
  115. package/dist/routes/operator.js +4 -3
  116. package/dist/routes/shared.d.ts +8 -8
  117. package/dist/routes/shared.js +9 -9
  118. package/dist/routes/ui-config.js +1 -1
  119. package/dist/routes/ui.js +1 -1
  120. package/dist/runtime/call-admission.js +1 -1
  121. package/dist/runtime/call.js +1 -1
  122. package/dist/runtime/settlement.js +1 -1
  123. package/dist/runtime/shared-read.js +2 -15
  124. package/dist/sent-secrets.d.ts +1 -2
  125. package/dist/sent-secrets.js +3 -8
  126. package/dist/server.js +4 -4
  127. package/dist/skills.js +8 -35
  128. package/dist/sqlite.d.ts +0 -19
  129. package/dist/sqlite.js +2 -83
  130. package/dist/storage/keys.js +16 -125
  131. package/dist/storage/sql-native.js +0 -101
  132. package/dist/storage/sql.d.ts +1 -24
  133. package/dist/storage/sql.js +1 -12
  134. package/dist/tool-error.js +4 -0
  135. package/dist/url-safety.js +0 -4
  136. package/dist/usage-guide.js +2 -2
  137. package/dist/version.d.ts +1 -1
  138. package/dist/version.js +1 -1
  139. package/dist/worker.js +1 -1
  140. package/documentation/README.md +6 -42
  141. package/documentation/architecture.md +86 -496
  142. package/documentation/auth.md +12 -25
  143. package/documentation/code-mode.md +90 -66
  144. package/documentation/deploying.md +19 -40
  145. package/documentation/integrating.md +4 -19
  146. package/documentation/meta-tools.md +6 -6
  147. package/documentation/migration-0.33.md +5 -5
  148. package/documentation/migration-0.34.md +2 -4
  149. package/documentation/migration-retired-layouts.md +74 -0
  150. package/documentation/operator-tests.md +19 -119
  151. package/documentation/operator-ui.md +3 -4
  152. package/documentation/provider-auth-migration.md +4 -10
  153. package/documentation/provider-migration-0.29.md +4 -15
  154. package/examples/worker/AGENTS.md +4 -6
  155. package/examples/worker/README.md +20 -115
  156. package/guides/connecting-agents.md +7 -30
  157. package/guides/how-it-works.md +2 -2
  158. package/guides/safety-and-trust.md +4 -1
  159. package/package.json +19 -19
  160. package/templates/node/README.md +9 -26
  161. package/templates/node/package.json +1 -1
  162. package/dist/auth/oauth-v2-migration.js +0 -135
  163. package/dist/provider-call-facts.d.ts +0 -115
  164. package/dist/provider-call.d.ts +0 -112
  165. package/dist/storage/d1-copy.js +0 -255
  166. package/dist/storage/sql-schema.d.ts +0 -6
  167. package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
  168. package/examples/worker/scripts/copy-kv-to-d1.mjs +0 -162
@@ -31,9 +31,8 @@ implementation boundary. Use these sections for the affected subsystem.
31
31
  | Find code and deployment evidence | [Source map](#where-else-to-look), [sharp edges](#sharp-edges), [repository policy](https://github.com/zackbart/connecta/blob/main/AGENTS.md) |
32
32
 
33
33
  Source paths and test names in each section identify its implementation and
34
- regression evidence. Recorded production probes and bundle measurements are
35
- historical observations tied to their stated date or version, not current
36
- benchmarks or proof that planned replacements have shipped.
34
+ regression evidence. Decision records preserve historical measurements; current
35
+ contracts and executable evidence describe the implementation.
37
36
 
38
37
  ## The two lifetimes
39
38
 
@@ -226,7 +225,7 @@ An admitted non-preflight `/mcp` request then takes five steps in
226
225
  ([#178](https://github.com/zackbart/connecta/issues/178)) but their URLs were
227
226
  handed out, and retiring a scoping boundary into fail-open is worse than any
228
227
  404 — then register the six meta-tools on a fresh `McpServer`
229
- (`test/server.test.ts`, `test/code-first-surface.test.ts`).
228
+ (`test/server.test.ts`).
230
229
 
231
230
  ### Modern request metadata and transport refusals
232
231
 
@@ -443,139 +442,24 @@ Only an omitted or `undefined` `callAdmission` selects a connector's default.
443
442
  The shared schema rejects malformed defined policies at factory construction,
444
443
  and the registry validates custom connector policies before registration.
445
444
  Falsy values such as `false`, `null`, `0`, and `""` are construction errors
446
- (INV-11), never a way to disable admission. The generated provider fixtures
447
- exercise these refusals and unchanged defaults in
448
- `test/call-admission-options.test.ts`.
445
+ (INV-11), never a way to disable admission.
449
446
 
450
447
  ### Production Worker disconnects
451
448
 
452
- The original production probe for [#573](https://github.com/zackbart/connecta/issues/573)
453
- ran on 2026-09-25 with compatibility date `2025-01-01`, first with no flags and
454
- then with `enable_request_signal`. It used Wrangler 4.114.0 and a Node 26.9.0
455
- client. A raw Worker stream established the runtime behavior; a custom auth
456
- provider returning a synthetic streaming 401 exercised Connecta's real admission
457
- wrapper. Each case had its own Connecta instance with one request permit and no
458
- queue. Health checks and subsequent MCP requests confirmed the same isolate ID.
459
- The [recorded observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-2026-09-25.json)
460
- contain all twelve cases, including served flags and client-end timing.
461
-
462
- | Response and flag | Raw stream abort / cancel | Connecta source abort / cancel | Admission after response; next request |
463
- | ----------------------------------------------------- | ------------------------- | ------------------------------ | -------------------------------------- |
464
- | Heartbeats, no flag | neither | neither | active 1; 503 |
465
- | Idle with a pending timer, no flag | neither | neither | active 1; 503 |
466
- | Heartbeats, request signal enabled | abort only | both | active 0; 200 |
467
- | Idle with a pending timer, request signal enabled | abort only | both | active 0; 200 |
468
- | One chunk, then no timer or I/O, either configuration | neither | neither | active 1; 503 |
469
-
470
- The last row is a different failure. The client observed the response end
471
- _before_ its planned disconnect, although the source never closed itself.
472
- The runtime ended the response without running JavaScript cleanup. This does
473
- not show a missing abort on a still-live request. The other cases remained open
474
- until the client aborted. Admission was sampled 1.5 seconds later, followed by
475
- an actual MCP request; this measures retained capacity, not an infinite leak.
476
-
477
- Enable `enable_request_signal` when deploying on Workers: it let Connecta's
478
- abort listener cancel the source and release admission for live responses in
479
- this experiment. It does not solve runtime-ended streams. The admitted-request
480
- bound above recovers capacity on the first later request at or after its
449
+ Enable `enable_request_signal` when deploying on Workers so Connecta can cancel
450
+ the source and release admission for live responses. It does not solve
451
+ runtime-ended streams. The admitted-request bound above recovers capacity on the first later request at or after its
481
452
  deadline, even if the old request's timer never ran. This is capacity recovery,
482
453
  not proof that workerd cancelled an old stream. A still-live request reaches
483
454
  the same hard deadline and is aborted in its own context.
484
455
  Cloudflare documents the flag in its
485
456
  [Request API](https://developers.cloudflare.com/workers/runtime-apis/request/).
486
457
 
487
- ### Authentication release by collection on workerd
488
-
489
- Measured on 2026-10-20 with workerd 1.20260722.1 (Miniflare 4.20260722.0),
490
- compatibility date `2025-01-01`:
491
-
492
- - `FinalizationRegistry` and `WeakRef` are undefined without
493
- `enable_weak_ref`; the flag's default-on date is 2025-05-05.
494
- - With `enable_weak_ref` and `enable_request_signal`, an object reachable only
495
- through a fetch continuation is finalized once its invocation ends and
496
- workerd cancels the fetch; while the invocation runs it is not. Pending
497
- timers and listeners on the request's signal do not keep it.
498
- - Strong values in a WeakMap keyed by a `Request` (even one the handler
499
- created), properties set on a `Request`, and WeakMap values keyed by its
500
- `Headers`, `ctx` or `env` stay reachable after the invocation ends. The
501
- lifetime registry in `src/auth-admission.ts` therefore stores `WeakRef`s.
502
- - No `gc()` is exposed. Allocating large arrays (32 × 1 MiB per request)
503
- triggers collections, but the most recent unreachable object often survives
504
- them; V8 in this build scans the native stack conservatively. The inspector's
505
- precise `HeapProfiler.collectGarbage` freed it.
506
- - In the vitest-pool-workers harness, an isolate that had already collected
507
- one scenario's cancelled work sometimes kept a later scenario's cancelled
508
- work reachable through 400 rounds of pressure; standalone Miniflare never
509
- did. `test/auth-admission-binding.test.ts` gives each collection scenario a
510
- fresh isolate and requires two of four sentinels collected before asserting
511
- that live charges stayed counted.
512
-
513
- A second production run on the same date tested the request bound at 2,000 ms.
514
- All twelve raw-stream and MCP cases passed with both flag configurations. Live
515
- MCP requests still rejected a competing request with 503 before the deadline;
516
- the first MCP request after the deadline returned all eight tools with 200 in
517
- the original isolate. Admission then reported zero active requests. Without the
518
- flag, the source still recorded no abort or cancellation, so recovery did not
519
- rely on runtime cleanup. The [recovery observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-recovered-2026-09-25.json)
520
- also retain two inconclusive queue-handoff cases: concurrent client connections
521
- reached different isolates and could not establish an abandoned queued waiter.
522
- A [second trial using HTTP/2](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-h2-2026-09-25.json)
523
- repeated the twelve passing core cases and verified queued cancellation with
524
- request signals enabled. Even on one HTTP/2 connection, the no-signal queue case
525
- crossed isolates and remained inconclusive. The Node and workerd regression
526
- suites cover expired waiters, orphaned handoffs, and identity-safe late release
527
- deterministically.
528
-
529
- The [historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect.mjs)
530
- and observations are retained in Git history, outside the current checkout and
531
- npm package. The probe deployed a disposable Worker containing only synthetic
532
- data, verified the served configuration, saved observations, and deleted the
533
- Worker in `finally`. It used no `waitUntil`, which would change the lifetime
534
- being measured. The production runtime build number was not exposed by the
535
- deployment; these findings are tied to the date and flags above.
536
-
537
458
  ### Production Access bypass for agent endpoints
538
459
 
539
- P0 of [#837](https://github.com/zackbart/connecta/issues/837) asked whether
540
- `/agent/*` can skip a Worker-level Cloudflare Access application, so an agent
541
- sends only its `cta_` token, while every other path keeps Access and
542
- `ctx.access`. It can. The probe ran on 2026-10-15 against a disposable Worker on
543
- its `workers.dev` hostname, with compatibility date `2026-09-01`, `cf`
544
- 1.0.0-beta.14, Wrangler 4.114.0, and curl 8.7.1 with `--path-as-is`, so raw
545
- paths reached the edge unnormalized. The Worker-level application allowed only
546
- a probe service token. Its unauthenticated refusal was therefore Access's 403
547
- page rather than a login redirect. The [observations](https://github.com/zackbart/connecta/blob/main/scripts/probes/access-agent-bypass-2026-10-15.json)
548
- record three identical rounds after each configuration change had produced
549
- three consecutive matching responses.
550
-
551
- - **Precedence.** A self-hosted application whose destination is
552
- `{ "type": "public", "uri": "<host>/agent/*" }`, with one `bypass` policy
553
- including `everyone`, overrides the Worker-level application on matching
554
- paths only. Unauthenticated requests reach the Worker there. `/mcp`, `/`,
555
- and every unmatched path still get the Access refusal. A service token is
556
- still admitted on them with `ctx.access` from the Worker-level application.
557
- This confirms Cloudflare's documented hierarchy, and that Worker-level
558
- protection still covers the `workers.dev` hostname.
559
- - **`ctx.access` on a bypassed request is present, not absent.** Its `aud` is
560
- nonempty and is the _path_ application's AUD, not the Worker-level one.
561
- `getIdentity()` resolved to `undefined` for every credential tested: none, a
562
- valid service token, a `cta_`-style bearer token, and a forged
563
- `Cf-Access-Jwt-Assertion`. A service token on a protected path also gave
564
- `undefined` identity, carrying the Worker-level AUD. A human
565
- `CF_Authorization` cookie on a bypassed path was not tested, so what
566
- `getIdentity()` returns there is unknown. That is one more reason the agent
567
- route ignores Access context rather than relying on its contents.
568
- - **API shape.** A Worker destination is exclusive to one application. A
569
- second application with `{ "type": "worker", "worker_id" }` plus `uri` or
570
- `path` was refused with 409 `destination belongs to another application`. The
571
- bypass must be hostname-and-path, stored with domain `<host>/agent/*`, and it
572
- covers only that exact hostname.
573
- - **Propagation.** Creating or deleting each application took 15–19 seconds to
574
- settle across runs. While a deployment or application change was propagating,
575
- the edge returned transient 500 (`error code: 1104`) and 404 (`error code:
576
- 1042` or `1043`) responses. The recorded run has one such 404, on the
577
- Worker-level-only service-token control for `/agent/x`. Deleting the path
578
- application restored the 403 on `/agent/x`.
460
+ Access uses a hostname-and-path bypass application for `/agent/*`; the
461
+ Worker-level application protects other paths. A bypassed request can still
462
+ carry `ctx.access`, so agent routes ignore that context.
579
463
 
580
464
  | Raw request path | Access outcome | Worker pathname (+ search) |
581
465
  | -------------------------- | -------------- | -------------------------- |
@@ -607,139 +491,32 @@ leading `//`, and treats `%2f` as data, not a separator. The effective Worker
607
491
  pathname, `new URL(request.url).pathname`, is the raw request path with case,
608
492
  escapes, and slashes untouched, except that dot segments arrive resolved:
609
493
  `/mcp/../agent/x` arrives as `/agent/x`. Every path that
610
- resolves outside `/agent/*`, including every `/mcp` form, stayed protected. The
494
+ resolves outside `/agent/*`, including every `/mcp` form, remains protected. The
611
495
  inverse is the risk: Access bypasses paths
612
496
  that connecta must not treat as an agent pool. Connecta's router compares raw
613
497
  pathnames without decoding, case folding, or slash collapsing, so an `/agent/`
614
- route must keep that property. It should accept exactly
615
- `/agent/<pool>`, with `<pool>` byte-equal to a declared pool name. It should
616
- refuse an empty segment, a trailing slash, any extra segment, `%`, uppercase
617
- variants, and a leading `//`, all with the same 404. It must ignore
498
+ route keeps that property. It accepts exactly
499
+ `/agent/<pool>`, with `<pool>` byte-equal to a declared pool name. It refuses
500
+ an empty segment, a trailing slash, any extra segment, `%`, uppercase
501
+ variants, and a leading `//`, all with the same 404. It ignores
618
502
  `ctx.access` entirely, since a bypassed request still carries an AUD.
619
503
  [Agent endpoints](./auth.md#agent-endpoints) implement exactly this in
620
504
  `src/routes/agent-path.ts`, and `test/agent-endpoints.test.ts` replays every
621
505
  row of the table above. The hyphenated row stands for any two-part pool name
622
- such as `<agent>-<user>`. The pool names in the recorded paths were replaced
623
- with placeholders after the run; the edge behavior for that path shape was
624
- unaffected.
625
-
626
- To configure the bypass, keep the Worker-level application described in
627
- [Cloudflare Access on Workers](./auth.md#cloudflare-access-on-workers). Then,
628
- for each hostname agents use (the `workers.dev` hostname, each Custom Domain,
629
- or route host), add a self-hosted Access application. In the dashboard, set
630
- domain to that hostname and path to `agent/*`, with one policy whose action is
631
- **Bypass** and whose include rule is **Everyone**. Through the API, that is:
632
-
633
- ```json
634
- {
635
- "type": "self_hosted",
636
- "name": "connecta agents",
637
- "destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
638
- "policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
639
- }
640
- ```
506
+ such as `<agent>-<user>`.
641
507
 
642
- Only the `workers.dev` hostname was probed. A Custom Domain application is the
643
- same documented hostname-and-path form, but it was not observed here. Do not
644
- rely on a fixed wait after a change. Changes settled in 15–19 seconds, yet one
645
- earlier round still saw a 403 on a bypassed path more than 30 seconds after
646
- the change. Test only after the positive and negative controls agree together
647
- for several consecutive rounds. The positive controls are bypassed paths
648
- reaching the Worker with the path AUD and a service token reaching a protected
649
- path with the Worker-level AUD; the negative controls are `/mcp` and `/`
650
- returning the Access refusal. The current script requires five such rounds
651
- after creating the bypass and three after other changes. The recorded run
652
- predates this: after the bypass it converged only the positive `/agent/x`
653
- control, then waited a fixed 90 seconds, and its negative controls were checked
654
- in the recorded rounds rather than before them.
655
-
656
- To repeat from a repository checkout, run
657
- `node scripts/probes/access-agent-bypass.mjs --profile <name> [output.json]`.
658
- `--profile` is required and has no default; every `cf` call passes it
659
- explicitly. Name the profile for the account that should host the probe; it
660
- needs
661
- Access application, policy, and service-token writes, plus Workers script
662
- writes. The script deploys and deletes the Worker with `cf`. `cf deploy` builds
663
- through Wrangler 4.136.0 or newer, so the probe installs Wrangler 4.145.0 into
664
- its temporary project. Wrangler needs no login. The script records only the
665
- count of existing Access applications, then names everything it creates
666
- `connecta-probe-<run>`. It force-deletes and verifies each resource in
667
- `finally`, then checks that the non-probe application ids are unchanged.
668
- Observations record statuses, outcome classes, pathnames, and which
669
- application's AUD matched, compared by digest inside the Worker. They contain no
670
- hostnames, account ids, tokens, cookies, or JWTs. `cf` delete commands prompt
671
- for confirmation and exit 0 without deleting when stdin is not a terminal, so
672
- the script passes `--force`. `cf deploy` printed `Deploy complete` and then
673
- kept running, so the script stops it after that line. The recorded run deployed
674
- with Wrangler 4.114.0 and used `cf` for Access. A standalone deploy and delete
675
- verified the `cf` deploy and delete path. The full probe has not been rerun on
676
- that path.
677
-
678
- Every subprocess (`cf`, `curl`, `npm`) runs through one runner. It enforces a
679
- deadline by killing the process group, keeps stdout and stderr in memory, and
680
- returns only facts: success, exit code, timeout, HTTP status, numeric API
681
- code, and an error slug from a fixed set (anything else is `unknown`). Commands
682
- that return data are parsed inside the runner against schemas that copy only
683
- whitelisted fields: ids, names, counts, booleans, and the Worker's `ctx.access`
684
- summary. Credentials stay in a holder that prints as `[secret]`. An unexpected
685
- shape, including `null` where a list belongs, is a failure, never an empty
686
- result. Errors are fixed diagnostics plus those facts. Teardown deletes every
687
- tracked id on its own. Then Access applications, service tokens, and Workers
688
- each get their own bounded discovery-and-delete pass by name, for resources
689
- whose create response was lost, so a failure in one family never skips
690
- another. Each discovered resource is deleted independently. Access policies are
691
- inline on their applications and go with them. A resource counts as gone only
692
- when a successful lookup omits it, and the final inventory verifies each family
693
- separately. Run
694
- `node scripts/probes/access-agent-bypass.mjs --self-test` to check the runner
695
- offline. It feeds canary secrets, suffixed slugs, malformed JSON, and a stalled
696
- process tree through it. It also runs teardown against a fake `cf` whose
697
- application listing always fails, after a lost token-create response, and
698
- checks that the token is still found and deleted. It also checks that a Worker
699
- that refuses deletion does not block another Worker's deletion. It runs no real
700
- `cf`, `curl`, or network command. This
701
- hardening and the joint convergence are verified offline only and have not run
702
- against Cloudflare.
508
+ For the operator procedure and Access application JSON, see
509
+ [Agent endpoints behind Cloudflare Access](./deploying.md#agent-endpoints-behind-cloudflare-access).
703
510
 
704
511
  ### Alchemy Worker deployment
705
512
 
706
- Two throwaway deployments on 2026-10-20 verified the Worker example's
707
- Alchemy path with alchemy 2.0.0-beta.83, effect 4.0.5, and Alchemy local
708
- state, then deleted every resource and confirmed by re-listing. Alchemy
709
- uploads the full Worker with `PUT /workers/scripts/<name>`, the endpoint an
710
- earlier full deploy had been suspected of losing `ctx.access` through.
711
-
712
- The first deployment ran a near copy of the stack with a temporary diagnostic
713
- route. With an Access service token in a non-identity policy, the Worker saw
714
- `ctx.access` carrying the Worker-level application's AUD and no identity, as
715
- expected for a service token. D1, the Worker Loader, R2, and secret bindings
716
- worked; `/agent/*` reached connecta through the bypass; `/ui/api/config`
717
- returned 403 `Cloudflare Access human identity required`; and
718
- `connecta doctor` passed with a `cta_` token issued inside the Worker. A code
719
- update through `alchemy deploy` kept the script tag, `ctx.access`, and D1 rows.
720
- Three consecutive probe rounds agreed.
721
-
722
- The second deployment ran the shipped `alchemy.run.ts` from the packed package
723
- in a separate project on Node 26, changing only resource names and the Access policy
724
- (a service token). The edge checks in the example's "Verify after deploying"
725
- held for three or more rounds after each deploy. `alchemy destroy` deleted the
726
- Worker and both Access applications and orphaned the retained D1 database; the
727
- next deploy adopted it by name with its rows. After deleting local state, a
728
- deploy reused the Worker with the same script tag, and `applicationId` took
729
- both Access applications over with unchanged AUDs and no duplicates. Removing
730
- `applicationId` afterwards planned an in-place update, and a further deploy
731
- planned no changes. The account API token needed only Workers Scripts Write,
732
- D1 Write, Access: Apps and Policies Write, and Access: Organizations, Identity
733
- Providers, and Groups Read.
734
-
735
513
  Four findings shape the operator rules. Alchemy local state under
736
514
  `.alchemy/state/` records secret binding values in plaintext. Alchemy sends no
737
515
  `keep_bindings`, so each deploy deletes secrets set outside the stack.
738
516
  `alchemy dev` does not provide `ctx.access` to a plain fetch Worker, so local
739
517
  development stays on `wrangler dev` and its `access.dev` identity. D1 needs
740
518
  `RemovalPolicy.retain()` so `alchemy destroy` cannot delete a deployment's
741
- state. Not verified: `Cloudflare.state()` remote state, custom domains, and
742
- taking over a Worker first deployed with Wrangler.
519
+ state.
743
520
 
744
521
  npm strips `.gitignore` from the package, so the copied Worker example's
745
522
  ignore rules ship as `examples/worker/gitignore`, renamed by the README's copy
@@ -760,15 +537,8 @@ activity table beside it. `src/storage/sql.ts` owns the Promise edges.
760
537
  Each store creates its tables on first use. `memoryStorage()` is the default
761
538
  and the test double. Workers KV and the 0.28 JSON file store are gone: KV is
762
539
  eventually consistent and cannot compare-and-set, and the file store rewrote
763
- its whole file on every write. Each has a one-shot copy into its replacement,
764
- `copyKvToD1` on `/d1` and `connecta migrate-state` on Node. Both copy live
765
- entries verbatim, because physical keys did not change, and both keep any key
766
- the target already holds. `copyKvToD1` reports counts per key family
767
- (`familyOfKey` in `src/storage/keys.ts`), never a key. It resumes through a
768
- source-bound, atomically claimed token, because Workers KV's own list cursor
769
- can spell a key. Copy under maintenance after KV stabilizes, verify hashes
770
- and expiries, and mark cutover before reopening traffic; the permanent D1
771
- marker refuses stale copies afterward.
540
+ its whole file on every write. Their one-shot copies were removed; deployments with old state must finish
541
+ conversion on 0.37.1 before upgrading. See [Retired layout migrations](./migration-retired-layouts.md).
772
542
 
773
543
  `KVStorage` is `get`/`set`/`delete`/`list(prefix)`/`compareAndSet`, all
774
544
  required, and `createConnecta` refuses storage missing one (INV-11).
@@ -808,12 +578,10 @@ Keys and list prefixes must not contain U+0000 (NUL). D1, SQLite, and memory
808
578
  storage reject them with `TypeError` before accessing storage: Node 22's
809
579
  `node:sqlite` truncates TEXT results at NUL. Builders reject NUL in unencoded
810
580
  components.
811
- State-file import validates all keys before writing. The `connecta_kv` table
812
- keeps its existing TEXT keys, including compatibility with the 0.28 schema.
813
- Storage writes no log lines. A refused import names the file and an entry's
814
- position, never a key, a value, or the JSON parser's account, which quotes
815
- the file (INV-6). Database failures report only a fixed import step and an
816
- entry's position, including errors raised by target-database triggers.
581
+ The `connecta_kv` table keeps its existing TEXT keys, including compatibility
582
+ with the 0.28 schema. Storage writes no log lines. JSON state-file import is
583
+ retired; deployments needing it must use the 0.37.1 bridge release before
584
+ upgrading. See [Retired layout migrations](./migration-retired-layouts.md).
817
585
 
818
586
  Remote MCP catalogs use the SDK `ResponseCacheStore` over this same SQL KV.
819
587
  The `response-cache:v1:` family binds connector id, a hash of its configured
@@ -1283,9 +1051,8 @@ parse as text, so connecta reads downstream bodies as bytes
1283
1051
  (`src/byte-read-response.ts`), `ctx.oauth.fetch()` answers included. The
1284
1052
  operator page may show catalog metadata the operator loaded (descriptions,
1285
1053
  schemas), but withholds a tool name outside the grammar as it does in records.
1286
- `test/operator-record-sources.node.test.ts` is a secondary lint over every other
1287
- log call in `src/`. Fix the sink, not the source: a filter at each source missed
1288
- the next one. Recorded HTTP status facts survive the bare write-401 fallback,
1054
+ Fix the sink, not the source: a filter at each source missed the next one.
1055
+ Recorded HTTP status facts survive the bare write-401 fallback,
1289
1056
  Google's uncertain-write wording, and oversized-response mapping; see
1290
1057
  [Read failure records](./operating.md#read-failure-records) for their operator
1291
1058
  meaning. These facts do not change agent error fields or write outcomes.
@@ -1456,9 +1223,7 @@ acknowledgement, then reconciles vendor state with exactly one physical send.
1456
1223
  Focused provider suites retain MIME/draft restrictions, revision conflicts,
1457
1224
  CCB routing, Planning Center product exclusions and deployment pins. Native
1458
1225
  HTTP framing, cursor and request-lifetime fault probes remain in the HTTP suites.
1459
- The catalog-wide status sweep and its argument roster, catalog/ranking copies
1460
- and unused paging strategy tests are retired under #1072. Provider workflows
1461
- belong in the existing shared outcomes; a new tool requires no failure-matrix row.
1226
+ Provider workflows belong in the existing shared outcomes.
1462
1227
  No legacy REST tool adapter remains.
1463
1228
  Large documents opt into generator options that drop restated operation ids
1464
1229
  and plain path parameters and cap one operation's details (`opBudget`).
@@ -1732,8 +1497,7 @@ path, or fields a schema leaves undeclared; such paths still apply at
1732
1497
  runtime).
1733
1498
 
1734
1499
  `test/fixtures/value-safety.ts` is the shared harness each vendor's
1735
- `provider.safety.node.test.ts` runs; `reviewProblems` is pure, and
1736
- `test/value-safety-harness.node.test.ts` proves wrong paths fail it. It fails
1500
+ `provider.safety.node.test.ts` runs; `reviewProblems` is pure. It fails
1737
1501
  on candidates from another pin, format, or option set; an unreviewed
1738
1502
  candidate; a verdict for an operation the index lacks; a table path the stamp
1739
1503
  lacks (unresolved, or edited since `providers:spec`); a flagged field no path,
@@ -1761,12 +1525,6 @@ and reports only added, removed and changed counts in the caller's partition.
1761
1525
 
1762
1526
  #### Behavioral evidence (contributor)
1763
1527
 
1764
- The characterization snapshot engine, provider JSON snapshots, source scheduler
1765
- and its self-tests are retired under
1766
- [#1072](https://github.com/zackbart/connecta/issues/1072). Provider changes no
1767
- longer require golden enrollment, snapshot updates, byte equivalence or nightly
1768
- capture determinism. Production invariants remain unchanged.
1769
-
1770
1528
  Use the existing stateful fixture vendors and published-edge deployment adapters:
1771
1529
 
1772
1530
  - `pnpm run eval:outcomes` drives representative joins, retained paging, endpoint
@@ -1794,14 +1552,6 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1794
1552
  returned private values and credential echoes; positive record facts must
1795
1553
  still arrive. An explicitly writable pool also exercises reopening a
1796
1554
  read-only deployment without bypassing connector policy.
1797
- - `test/cancellation-journey.node.test.ts` observes a queued client's departure
1798
- without another vendor send, and an active client's departure after the
1799
- vendor commits. The vendor must see its held response socket close before
1800
- releasing it; a later MCP read recovers the committed intent exactly once.
1801
- `test/cancellation-faults.test.ts` keeps signal-less token-resolution,
1802
- warmed-credential digest, stalled body-cleanup and abort/grant races on Node
1803
- and workerd. These replace the universal Promise-method/seam/owner matrix
1804
- and its enrollment self-test, plus migration-only runtime wrapper checks.
1805
1555
  - `test/browser/operator-flows.spec.ts` serves the actual operator shell and
1806
1556
  routes over TCP with file-backed SQLite. Sign-in, Settings provenance, OAuth
1807
1557
  connect/disconnect and credential save/test/replace/remove survive closing and
@@ -1835,17 +1585,10 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1835
1585
  after HTML, malformed JSON and oversized acknowledgements with one physical
1836
1586
  send. It distinguishes Stripe's reviewed pre-execution 401 refusal, and checks
1837
1587
  read failure facts, secrecy and recovery through direct and QuickJS calls.
1838
- `test/provider-failure-sweep.test.ts` retains only concrete auxiliary, composite,
1839
- in-flight and untracked-call fault probes; no provider/tool argument roster or
1840
- catalog/finalizer agreement sweep remains. Existing
1841
- `test/sent-secrets.test.ts`, `test/sent-secret-output-boundary.test.ts`,
1842
- `test/request-secret-redaction.test.ts` and `test/catalog-secret-redaction.test.ts`
1843
- cover secrecy before projection, truncation, caching, paging and diagnostics.
1844
- - `test/write-evidence.node.test.ts`, `test/remote-mcp-attempts.node.test.ts` and
1845
- `test/oauth-credential-attempts.node.test.ts` inspect physical socket sends.
1846
- Native transport parity, OAuth and write-provenance suites retain their
1847
- workerd coverage. The shared throwaway RSA key in `test/fixtures/rsa.ts` is
1848
- used by write-provenance and unknown-write-advice fixtures.
1588
+ Existing `test/sent-secrets.test.ts` covers secrecy before projection,
1589
+ truncation, caching, paging and diagnostics.
1590
+ - The shared throwaway RSA key in `test/fixtures/rsa.ts` is used by
1591
+ write-provenance and unknown-write-advice fixtures.
1849
1592
  - `test/executor-requests.test.ts` runs overlapping `execute_code` programs
1850
1593
  as separate workerd requests through the real Worker Loader executor, with
1851
1594
  real TCP host calls that read their own request's signal. Queued programs
@@ -1860,23 +1603,21 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
1860
1603
  `test/request-context.node.test.ts` pins the same wake order and async context
1861
1604
  on Node for executor and call admission and QuickJS host calls, and gives back
1862
1605
  grants that shutdown overtook.
1863
- - The request, catalog, auxiliary-call and renewal lifetime
1864
- suites, plus `test/auth-admission-binding.test.ts`, exercise cancellation,
1865
- held native work and garbage collection. These test actual request ownership;
1866
- the retired capture scheduler's source guards were only harness constraints.
1606
+ - `test/request-context.node.test.ts` and `test/auth-admission-binding.test.ts`
1607
+ exercise cancellation, held native work and garbage collection. These test
1608
+ actual request ownership.
1867
1609
 
1868
1610
  The remaining probes defend distinct reachable failures: auth admission and
1869
1611
  native scheduler ownership, grant/callback fencing and refresh coordination,
1870
1612
  SDK replay/redirect/SSE attempts, nested write provenance and refusal evidence,
1871
1613
  unawaited program drain and failed-program accounting, catalog drift and paging
1872
1614
  revocation, and hostile serializers, structural echoes, truncation and persisted
1873
- pages. Broad scripted-executor policy/privacy cross-products and repeated
1874
- post-401 result/error-shape tables are retired. Protocol claims link executable
1615
+ pages. Protocol claims link executable
1875
1616
  evidence files in `spec/coverage.json`; the invariant contracts above are defended
1876
1617
  by the actual behavior assertions in these journeys and focused probes.
1877
1618
  The required CI gate includes build/declarations, Node and Workers probes,
1878
1619
  outcomes, browser flows, installed consumers and Docker, audits and real Eve
1879
- compatibility. Further contributor cleanup is tracked in #1072.
1620
+ compatibility.
1880
1621
 
1881
1622
  Add a compact fault journey only for a meaningful outcome those suites cannot
1882
1623
  observe. Reuse their fixtures and adapters; do not reintroduce a snapshot
@@ -1904,8 +1645,7 @@ behind the same redirect wrapper and a same-origin guard.
1904
1645
  client replacement, transport confinement and secrecy on Node, and a real
1905
1646
  service binding on workerd.
1906
1647
  `test/google-service-account-token.test.ts` exercises signing/cache behavior on
1907
- Node and workerd; `test/google-service-account-remote.node.test.ts` checks
1908
- real-socket token/MCP requests, invalidation and one physical attempt per write.
1648
+ Node.
1909
1649
  Operator setup lives in [Google hosted MCP](./auth.md#google-service-account-tokens-and-hosted-mcp).
1910
1650
 
1911
1651
  `createConnecta` takes closed typed `ui`, `vault`, and `activity`
@@ -2186,10 +1926,8 @@ request fixture and native-output suites supply runtime parity evidence.
2186
1926
 
2187
1927
  ### Native SQL storage (contributor)
2188
1928
 
2189
- `src/storage/sql-native.ts` composes KV, activity schema upgrades, retention,
2190
- and bounded SQL copies as Effects. `src/storage/d1-copy.ts` composes the
2191
- Workers KV copy, source-bound resume claims, verification, cutover guard, and
2192
- recovery. Public factories and copy functions retain their Promise contracts;
1929
+ `src/storage/sql-native.ts` composes KV, activity schema upgrades, and retention
1930
+ as Effects. Public factories retain their Promise contracts;
2193
1931
  custom Promise SQL drivers retain the fallback lift.
2194
1932
 
2195
1933
  `src/storage/sql-driver.ts` constructs a public Effect 4.0.5 `SqlClient`
@@ -2208,17 +1946,8 @@ is the existing exact `effect` pin. Connecta does not adopt `@effect/sql-d1`:
2208
1946
  its 4.0.5 batch API drops metadata and its binding contract is wider than
2209
1947
  Connecta's published structural interface.
2210
1948
 
2211
- The D.2 architecture allowance was measured against `0e314247` using cached
2212
- official `node:22-slim` (Node 22.23.4, zlib 1.3.1-e00f703), no network, and
2213
- read-only bundle artifacts: `/d1` grew from 62,516 to 88,305 bytes gzip
2214
- (+25,789); the Worker example grew from 729,847 to 756,375 (+26,528).
2215
- Final typed-driver failure normalization measures 88,319 and 756,377 bytes
2216
- on that same official runtime. Executing real services and statement operations requires those runtime
2217
- semantics; the earlier identity-compiler facade did not provide them. The
2218
- explicit allowance changes only `/d1`'s cap from 62,542 to 90,000 bytes and
2219
- the Worker example's cap from 743,000 to 760,000. Historical baselines and
2220
- all other caps stay fixed. Final integration must remeasure the combined
2221
- implementation and satisfy the same compatibility and privacy contracts.
1949
+ Bundle caps and baselines are authoritative in
1950
+ [`scripts/bundle-budget.json`](https://github.com/zackbart/connecta/blob/main/scripts/bundle-budget.json).
2222
1951
 
2223
1952
  The platform connection capability contains no request state. D1 prepares in
2224
1953
  the current request and retains no prepared handles or promises. SQLite keeps
@@ -2245,14 +1974,11 @@ value, matching that fallback. Interruption remains interruption. The lookup
2245
1974
  module imports no SQL or platform implementation and does not change the
2246
1975
  published storage interfaces.
2247
1976
 
2248
- The synchronous state-file parser, validation, file read, and one atomic
2249
- SQLite import remain native synchronous leaves because `importStateFile` is a
2250
- published synchronous API. `openSqlite` retains file modes, WAL, busy timeout,
1977
+ `openSqlite` retains file modes, WAL, busy timeout,
2251
1978
  and database ownership. `test/sql-effect-contract.ts` retains affected-row,
2252
1979
  50-claim, typed-failure, cancellation, and tracing probes on SQLite and native
2253
- D1. `test/kv-to-d1.test.ts` also reuses stores across separate workerd requests;
2254
- the existing SQL contracts prove rollback, database-clock TTL, encodings,
2255
- activity upgrades, and copy recovery.
1980
+ D1. The existing SQL contracts prove rollback, database-clock TTL, encodings,
1981
+ and activity upgrades.
2256
1982
 
2257
1983
  ### Internal provider HTTP failures (contributor)
2258
1984
 
@@ -2293,7 +2019,7 @@ Stripe declares HTTP 409 as a conflict, alongside its fact-driven 429
2293
2019
  classification to an oversized 424 reply, preserving the dependency-failure
2294
2020
  meaning when the bounded body cannot be read. The original ceiling diagnostic
2295
2021
  and answered status remain; the core still decides write outcomes. It does not
2296
- apply to 401 or alter Q8. A fixed read-phase fact distinguishes failed body
2022
+ apply to 401 or change write-outcome settlement. A fixed read-phase fact distinguishes failed body
2297
2023
  reads from malformed JSON only for vendors declaring that wording; raw
2298
2024
  exceptions never enter diagnostics. Other providers retain their mapping and
2299
2025
  wording.
@@ -2310,14 +2036,13 @@ The mapping records each request's own served provenance and raw 401 facts.
2310
2036
  reservation's answer even for a read-classified tool issuing a physical write.
2311
2037
  An accepted write's blocked retry retains its original HTTP status; a slot
2312
2038
  occupied by an auxiliary read instead names the separate write step needed.
2313
- Structured resend advice reaches output only when the core permits it. Generic
2314
- unknown writes retain the core's diagnostic wait; Q8 removes it. The mapping
2315
- adds no Promise entry or lifetime owner. `provider-failure-sweep.test.ts`,
2316
- `guarded-attempts.node.test.ts`, `call-services.test.ts` and the native
2039
+ Structured resend advice reaches output only when the core permits it. Unknown
2040
+ write outcomes are never retryable; a generic one keeps the core's diagnostic
2041
+ `retryAfterMs`, and one settled by a 401 drops it. The mapping
2042
+ adds no Promise entry or lifetime owner. `call-services.test.ts` and the native
2317
2043
  transport suites exercise these contracts through invocation, physical sockets
2318
2044
  and request closure. `http-kit.test.ts` retains focused numeric/text fact,
2319
- request-equivalence and bulk-capacity fault probes. The migration-era
2320
- kit/core verdict comparison is retired under #1072. Maintained API providers use the kit through their
2045
+ request-equivalence and bulk-capacity fault probes. Maintained API providers use the kit through their
2321
2046
  explicit provider subpaths; it stays off the root import graph and published
2322
2047
  declarations expose Promises.
2323
2048
 
@@ -2328,8 +2053,7 @@ declarations expose Promises.
2328
2053
  are Promises. A connector is called with `Effect.tryPromise`, never asked to
2329
2054
  return an Effect.
2330
2055
  - **Inbound authorization.** `authorize` in `src/routes/shared.ts` ships in
2331
- the declarations and in the `./activity` bundle; converting it cost 32 KB
2332
- gzip there for nothing a caller could see.
2056
+ the declarations and in the `./activity` bundle.
2333
2057
  - **Pure synchronous code.** Ranking, schema rendering, classification,
2334
2058
  `validateToolInput`, the host-call budget, the emit collector, failure
2335
2059
  framing, and result shaping. The microtask scheduler never yields to I/O, so
@@ -3089,17 +2813,9 @@ including structural lookalikes and genuine router errors, retain their identity
3089
2813
 
3090
2814
  Four public `effect/http/*` modules are allowed only in the private dispatcher;
3091
2815
  all other root-purity and optional-implementation boundaries remain enforced.
3092
- Direct public module imports avoid the area's barrel cost. The local D3
3093
- measurement over the reviewed D1/D2 stack is root 373,435 to 393,470 bytes gzip
3094
- and Worker 758,274 to 774,384 (official Node 22.23.4/zlib 1.3.1-e00f703, identical
3095
- repository bundle options). The barrel alternative measures 423,348/787,614.
3096
- The parent accepted this measured native-routing cost with a Worker cap of
3097
- 785,000 bytes, including room for separately measured native-auth and reviewed
3098
- host/transport additions. Root and all other caps and historical baselines are
3099
- unchanged by D3. Separate-input costs are not a combined gzip measurement;
3100
- the integrated deployment must be measured again.
3101
-
3102
- `test/server.test.ts` asserts production routing directly on Node and workerd:
2816
+ Direct public module imports avoid the area's barrel cost.
2817
+
2818
+ `test/server.test.ts` asserts production routing directly on Node:
3103
2819
  raw prefixes and malformed suffixes, extension methods, CIMD before auth/UI/body
3104
2820
  reads, Response/error identity, unmatched fallthrough, concurrent request
3105
2821
  isolation, and body-held permits through end/failure/cancel/abort/abandonment.
@@ -3111,85 +2827,20 @@ Root purity, declaration and bundle checks own import boundaries and size caps.
3111
2827
  The frozen legacy dispatcher, source transforms, diagnostic latency loops and
3112
2828
  rejected Effect MCP adapters were migration evidence; their historical source
3113
2829
  is linked from decision 0016 rather than rebuilt by ordinary tests.
3114
- The upward MCP protocol leaves remain SDK implementations, with D7's separate
3115
- native-orchestration work and compatibility evidence unchanged.
3116
-
3117
- ### Why not Schema or HttpApi for core routes
3118
-
3119
- Both were measured and both lost. `effect` is imported through its barrel,
3120
- which defeats tree-shaking: the first conversion step measured about +20 ms
3121
- of Worker cold start for it, and that cost was accepted over deep imports at
3122
- every call site. The catch is that a module the barrel reaches is paid for in
3123
- full, however little of it is used. Effect Schema for config validation and the
3124
- meta-tool inputs came to about +62 KB gzip and +13 ms of cold start in the
3125
- root, for a validator no clearer than the hand-written schema in `src/config.ts`; the
3126
- meta-tool inputs stay zod, which the MCP SDK bundles regardless. `HttpApi` for
3127
- the operator and activity routes cost about +100 KB gzip on `./ui` and +130 KB
3128
- on `./activity`, and matching the wire format meant opting out of most of what
3129
- it does: unowned paths fall through rather than 404, a wrong method is a JSON
3130
- 405, and the content-type and size checks run before a body is read. The root
3131
- entry imports `effect` and the four public HTTP modules confined to the private
3132
- dispatcher (`test/purity.node.test.ts`).
3133
- Effect 4.x is a long-term-support line, but its area barrels `effect/http`,
3134
- `effect/http-api`, `effect/schema`, and `effect/sql` are still tagged
3135
- `@stability unstable` and can change within it; only core modules such as
3136
- `effect/Schema` are stable. That is why Effect is pinned to one exact version
3137
- and every Effect upgrade is its own deliberate pull request. A deployment
3138
- that also depends on Effect, such as the Worker example's Alchemy stack, must pin the
3139
- same version to resolve a single copy. Connecta's own lockfile and packed
3140
- install are checked for one copy (`test/package-surface.node.test.ts` and
3141
- `scripts/check-package.mjs`).
3142
-
3143
- The stable `4.0.0` release was re-evaluated on 2026-10-01 with this
3144
- [historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation.mjs),
3145
- retained in Git history rather than the current checkout.
3146
- The [raw observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation-2026-10-01.json)
3147
- are synthetic Node requests and neutral esbuild bundles, not live-client or
3148
- workerd lifetime verification. Bundle figures add representative prototypes
3149
- alongside the current code; they do not claim savings from removing old code.
3150
-
3151
- Two native input schemas with validation and JSON Schema rendering add 62,148
3152
- bytes gzip to the root, taking it from 303,083 to 365,231 against a 339,526
3153
- cap. Effect can reject excess properties when both decoding and rendering use
3154
- `onExcessProperty: "error"`; its default strips them. The generated schemas
3155
- still differ from the published meta-tool schemas, including record rendering and
3156
- composed numeric bounds. A Schema replacement would need to preserve that
3157
- contract and memoized rendering. Both MCP SDK packages still depend on Zod,
3158
- so changing our inputs alone does not remove Zod from an installation.
3159
-
3160
- A one-endpoint `HttpApi` prototype adds 106,634 bytes gzip to `./ui` and 106,453
3161
- to `./activity`. Even plain `HttpRouter` adds 45,324 to `./ui`, taking it to
3162
- 130,023 against a 128,983 cap. Both default handlers return an empty 404 for a
3163
- wrong method and an unowned path; our routes need a JSON 405 for the former
3164
- and module fallthrough for the latter. Their `toWebHandler` also builds its
3165
- layer immediately and owns a runner, so adopting it would require proving
3166
- global-scope safety and preserving our scheduler and response-lifetime rules.
3167
- Those historical defaults are not the production adapter: the narrow public
3168
- HttpRouter dispatch above preserves explicit method refusals, unowned fallthrough
3169
- and the existing edge runner without `toWebHandler`.
3170
-
3171
- Effect's MCP server has a separate compatibility gate. Its `2026-07-28`
3172
- adapter accepted a stateless `tools/list`, but its `2025-06-18` adapter refused
3173
- a fresh request with 400. Initialization issued a session, and that session's
3174
- next request returned 404 on a fresh handler; Connecta served the same fresh
3175
- legacy list with 200 JSON and no session. The stable package exports no MCP
3176
- client, so it also cannot replace our downstream SDK and OAuth implementation.
3177
- [Issue #622](https://github.com/zackbart/connecta/issues/622) records the server
3178
- parity requirements. These historical measurements supported retaining MCP protocol and validation
3179
- implementations. Production HTTP dispatch now uses the separately proven narrow
3180
- public adapter above; stable v4 alone did not establish its compatibility.
3181
-
3182
- The core's cost is recorded rather than guessed. Against 0.24.4 the root entry
3183
- grew from 235,346 to 279,526 bytes gzip, the Worker example from 263,949 to
3184
- 314,336, and each provider by about 36 KB, since each reaches Effect through
3185
- `remoteMcp()`. `pnpm run check:bundle` caps the deployed shapes (the root,
3186
- `./worker`, `./ui`, the operator UI assets, `./d1`, `./auth/access-tokens`,
3187
- the Worker example, and the other non-provider entries) against
3188
- `scripts/bundle-budget.json`. Per-provider entries are reported with their
3189
- size and change from baseline but not capped: every deployment already imports
3190
- the root, which carries the Effect core, so a provider's standalone bundle
3191
- counts that core a second time. An install carries about 53 MB of `effect`, with
3192
- no dependencies of its own.
2830
+ The upward MCP protocol leaves remain SDK implementations with native
2831
+ orchestration and compatibility evidence.
2832
+
2833
+ ### Validation and HTTP boundaries
2834
+
2835
+ Config validation uses `src/config.ts`; meta-tool inputs use Zod, which the MCP
2836
+ SDK also depends on. Core routing uses the narrow public HTTP adapter above.
2837
+ Effect is pinned exactly; deployments using Effect must resolve the same version.
2838
+ `test/package-surface.node.test.ts` and `scripts/check-package.mjs` check one copy.
2839
+ Per-provider bundles are reported, not capped, because their standalone graphs
2840
+ include the shared core again. Deployed non-provider shapes are capped by
2841
+ `scripts/bundle-budget.json`.
2842
+
2843
+ [Decision 0019](https://github.com/zackbart/connecta/blob/main/decisions/0019-core-validation-http-boundaries.md) records the rejected Schema/HttpApi measurements.
3193
2844
 
3194
2845
  ## Where else to look
3195
2846
 
@@ -3243,9 +2894,7 @@ compiling and configuring the real thing.
3243
2894
  accounting and claim ownership keep their existing ordering. Late
3244
2895
  rejections are observed; dispatch and lease guards still contain detached
3245
2896
  work. Per-await I/O cancellation remains useful for releasing resources,
3246
- while prompt refusal is enforced at the boundary
3247
- (`test/cancellation-faults.test.ts`, `test/operation-cancellation.test.ts`
3248
- and `test/cancellation-journey.node.test.ts`). Invocation owns
2897
+ while prompt refusal is enforced at the boundary. Invocation owns
3249
2898
  the recovery and logical-call records it reads after teardown. Closing an
3250
2899
  outer call also revokes nested calls paused in preparation. Request-owned
3251
2900
  MCP sessions and token flights outlive individual calls and close with the
@@ -3262,11 +2911,9 @@ compiling and configuring the real thing.
3262
2911
  transfers preparation, token exchange and fenced rotation commit to the
3263
2912
  storage coordinator under its own deadlines and deferred-work hook; only
3264
2913
  the caller's wait still closes with the call. That completion
3265
- cannot resume a closed call or resend its resource request
3266
- (`test/call-renewal-lifetime.test.ts`,
3267
- `test/remote-mcp-renewal-lifetime.test.ts`). Explicit request revocation also
3268
- aborts pending authorization-code exchange and refuses late token writes
3269
- (`test/operation-cancellation.test.ts`). Request closure
2914
+ cannot resume a closed call or resend its resource request. Explicit request
2915
+ revocation also aborts pending authorization-code exchange and refuses late
2916
+ token writes. Request closure
3270
2917
  revokes views and calls first, then runs every finalizer in reverse order,
3271
2918
  collecting failures. The caller's original cancellation signal is retained
3272
2919
  separately from the lease signal, so normal teardown never looks like caller
@@ -3340,13 +2987,13 @@ compiling and configuring the real thing.
3340
2987
  as a `manual` check in its `drift.json`): an API v1 answer carrying Stripe's
3341
2988
  `Request-Id`. The real HTTP hostile-response journey checks this refusal
3342
2989
  against an ambiguous committed write; there is no copied declaration list.
3343
- Q8 is decided in one place, `settleWrite` in
2990
+ Write outcomes are decided in one place, `settleWrite` in
3344
2991
  `src/write-outcome.ts`, once per logical call and over every request it
3345
2992
  recorded. The invocation calls it for every failure of a served write: a
3346
2993
  thrown or mapped error, a custom connector's, a program's call, and its
3347
2994
  own deadline or the caller's cancellation, when no connector failure
3348
2995
  arrives at all. It reads the logical call's requests
3349
- (`logicalCallRequests`), which hold every resource request dispatched
2996
+ (`LogicalLedger`'s `sent`), which hold every resource request dispatched
3350
2997
  under the call from any ledger: nested `api()` calls, remote MCP, guarded
3351
2998
  transports, `ctx.fetch` and the kit. Without an invocation (a direct call,
3352
2999
  or a context no invocation opened), only the outermost boundary calls it,
@@ -3415,9 +3062,8 @@ compiling and configuring the real thing.
3415
3062
  Remote MCP captures
3416
3063
  each physical read's entry before awaiting its response; a later send
3417
3064
  cannot overwrite it. The kit's response identity and Google's init identity
3418
- identify the request whose credential was renewed. The legacy guarded-fetch
3419
- `answeredRequest` helper has no production caller; tests retain it to exercise
3420
- renewal provenance outside the kit.
3065
+ identify the request whose credential was renewed. Guarded mappers that throw
3066
+ an authorization refusal identify its request through `refusalRequest(error)`.
3421
3067
  The decider normalizes every failure it settles,
3422
3068
  typed or not (a raw network error, an `AbortError`, a thrown string). Its
3423
3069
  verdict is final (`settledWrite`, which follows copies and redaction): the
@@ -3591,23 +3237,8 @@ compiling and configuring the real thing.
3591
3237
  unchanged for the invocation to report.
3592
3238
  Shared provider outcomes and the real HTTP hostile-response journey check
3593
3239
  returned data, failure/recovery, privacy and physical writes without a
3594
- catalog-wide status matrix. `test/provider-failure-sweep.test.ts` retains
3595
- fault probes for Docs creation with initial text, composite success followed
3596
- by refusal, GitHub preflight failures, typed handler errors after successful
3597
- sends, Forms cursor conflicts after 200, generic-code vendor refusals,
3598
- auxiliary failures after resource dispatch and concurrent direct-call isolation.
3599
- It also holds siblings awaiting credentials or already in flight when another
3600
- write fails, refuses requests after call closure, and probes network errors,
3601
- refusal-shaped abort reasons and untracked sends. Native transport and
3602
- request-lifetime suites retain their platform-specific evidence.
3603
- `test/write-evidence.test.ts` uses real refused, reset, and held sockets on
3604
- both runtimes: a refused write is unknown and a refused read retryable,
3605
- including a write aborted with another fetch's real refusal as its reason
3606
- after the server read its body; `test/write-evidence.node.test.ts` covers a
3607
- Planning Center POST that undici resends after a 421 to a closed listener, a
3608
- DELETE that a `RetryAgent` resends after a reset, and a streamed body and a
3609
- `Blob` with an overridden `stream()` that undici partly sends before its
3610
- producer fails. Each has the server holding the write and reports unknown.
3240
+ catalog-wide status matrix. Native transport and request-lifetime suites
3241
+ retain their platform-specific evidence.
3611
3242
  Additional tests:
3612
3243
  `test/remote-mcp-write-once.test.ts` (real SDK transport),
3613
3244
  `test/api-oauth.test.ts`, `test/google-workspace-delegation.test.ts`, and each
@@ -3694,10 +3325,10 @@ OAuth retry, grant, response-decoding or ownership orchestration.
3694
3325
  there before claiming a code/refresh grant, so an unsupported Node host consumes
3695
3326
  neither. The base-fetch leaves in static OAuth and remote MCP call
3696
3327
  `sendCredentialPost`; remote registration and disconnect revocation do so too.
3697
- D4 must preserve this preflight and the distinction between request-owned code
3328
+ The adapters preserve this preflight and the distinction between request-owned code
3698
3329
  exchange and coordinator-owned refresh after claim submission. The existing
3699
3330
  spent-token algorithm intentionally remains conservative after a submitted claim,
3700
- even if a later CONNECT fails before destination dispatch. D6 must preserve the
3331
+ even if a later CONNECT fails before destination dispatch. Remote MCP preserves the
3701
3332
  SDK JSON-RPC envelope distinction at the leaf: protocol POSTs are not credential
3702
3333
  POSTs; forms and registration JSON are. Discovery GETs remain native fetch.
3703
3334
 
@@ -3785,22 +3416,6 @@ unchanged. A narrow machine check restores only those two exact adjacent
3785
3416
  pairs and requires byte equality, with no other type, export or reference
3786
3417
  change. All 88 declarations still exclude Effect and have complete references.
3787
3418
 
3788
- The superseding 2026-10-25 D3/D4 cost ruling authorizes only Worker
3789
- `maxGzip: 785000` (previously 760000) and Clerk `maxGzip: 85000` (previously
3790
- 63087). Official cached Node 22.23.4 / zlib 1.3.1-e00f703 measured Clerk at
3791
- 47,688 -> 80,961 bytes gzip (+33,273): the generated SDK is unchanged, while
3792
- native Effect adds 91.8 KB raw plus the native helper and small auth-source
3793
- growth. The isolated D4 candidate measured Worker at 762,666, root at 377,177
3794
- and D1 at 88,321 bytes gzip. Optional-graph optimization brought UI to 342,955,
3795
- so the earlier unused UI allowance is revoked and its original 345000 cap
3796
- remains. Historical baselines, root/D1 and all other caps, dependency pins and
3797
- security/API gates remain unchanged. These independent measurements do not
3798
- predict a combined gzip by addition; final artifacts must be rebuilt. Native
3799
- registration, decorated fallback and request/native-storage semantics also
3800
- require a smoke through an actual minified production bundle. Final post-hook
3801
- official artifacts measure root 377,290, D1 88,321, Clerk 80,959, UI 342,959
3802
- and Worker 762,774 bytes gzip, all within those caps.
3803
-
3804
3419
  ### Native remote MCP programs (contributor)
3805
3420
 
3806
3421
  Remote MCP connection joins, credential and epoch checks, catalog intake,
@@ -3824,7 +3439,7 @@ restore that same owner. Every operation captures its own generation, credential
3824
3439
  identity and listing binding before waiting; a later listing cannot adopt an
3825
3440
  abandoned publication. Request closure revokes the connector and cache scopes.
3826
3441
  No client, transport, response, signal, promise or live request Context is shared
3827
- across inbound requests. D4's bounded refresh coordinator retains its separate
3442
+ across inbound requests. The bounded refresh coordinator retains its separate
3828
3443
  ownership, synchronous submission gate and waiting-request handoff.
3829
3444
 
3830
3445
  Two deliberate Promise waits remain: a connect waiter receives the Deferred's
@@ -3863,16 +3478,11 @@ select the underlying current methods at dispatch, including namespaces created
3863
3478
  while a source method is decorated. Original SQL shells call their own native
3864
3479
  program directly. Tests cover current and previously selected programs, own and
3865
3480
  inherited saved-original delegates, get/set/CAS and activity record/list,
3866
- restoration, typed failures and interruption. The D4 native proof fixtures install
3481
+ restoration, typed failures and interruption. The native auth proof fixtures install
3867
3482
  their intentional throwing Promise shells before registration, so they establish
3868
3483
  native composition without authorizing bypass of later replacements.
3869
3484
 
3870
- `remote-mcp-native.test.ts` runs native SDK/cache/request-boundary cases on Node
3871
- and workerd. `remote-mcp-native-bundle.node.test.ts` additionally loads an actual
3872
- minified production bundle to verify call/cache registration, request Context,
3873
- native storage, saved-original delegates, custom replacements and closed-owner
3874
- refusal. Existing wire, physical-attempt, cache and OAuth lifetime suites remain
3875
- the compatibility evidence.
3485
+ Existing wire, cache and OAuth suites remain the compatibility evidence.
3876
3486
 
3877
3487
  ### Native upward MCP orchestration (contributor)
3878
3488
 
@@ -3926,19 +3536,9 @@ identity, credential, storage, executor and tool callbacks. Connecta's OAuth-lin
3926
3536
  binding, TTL, progress, closing and one-use claims are native orchestration around
3927
3537
  those leaves. The supported protocol and unsupported-feature refusals remain
3928
3538
  unchanged. Native consumer, failure, decorator, sharing, close and minified-bundle
3929
- regressions live in `upward-native.test.ts` and
3930
- `upward-native-bundle.node.test.ts`; existing wire, continuation, redaction,
3539
+ regressions live in `upward-native.test.ts`; existing wire, continuation, redaction,
3931
3540
  physical-attempt and body/admission suites remain compatibility evidence.
3932
3541
 
3933
- The combined native/operator graph has a measured contributor bundle allowance
3934
- in PR #1060: the UI gzip cap is 346,000 bytes, with its historical baseline
3935
- unchanged. The official full checker measured 345,228 bytes after regenerating
3936
- the merged UI assets. Removing unused Invocation/Catalog registration retention
3937
- returned the public Worker entry from 198,744 to 77,426 bytes; live minified
3938
- Context, inherited-method, decorator and restoration proofs retain native
3939
- composition. Root, example Worker, public Worker, D1 and Clerk caps remain
3940
- unchanged. This cost decision grants no later request-body-limit allowance.
3941
-
3942
3542
  ### Catalog operation vocabulary (contributor)
3943
3543
 
3944
3544
  The standard hatch privately registers bounded vendor operation text against its
@@ -3953,13 +3553,3 @@ cache. Exact authored names and complete authored matches precede API pointers;
3953
3553
  ordinary result limits, offsets, catalog failure disclosure and byte bounds apply.
3954
3554
  The published discovery contract states compilation, query and cache bounds.
3955
3555
  No request identity, signal, credential or promise enters a vocabulary cache.
3956
-
3957
- ### Authenticated body-bound cost (contributor)
3958
-
3959
- The separate #860 change has a measured allowance only for the example Worker
3960
- gzip cap, from 785,000 to 786,000 bytes, with its historical baseline unchanged.
3961
- The official full checker measures 785,036 bytes against D7's exact 784,948-byte
3962
- lower graph, an actual 88-byte increase. It reuses the bounded platform body
3963
- inspector, preserves native orchestration and avoids a new reader lifecycle.
3964
- Root, UI, public Worker, D1, Clerk and asset caps remain fixed. The cost ruling
3965
- does not approve protocol, cancellation or authority semantics.