@zackbart/connecta 0.37.0 → 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.
- package/CHANGELOG.md +80 -0
- package/README.md +2 -2
- package/bin/connecta.mjs +1 -39
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +2 -4
- package/dist/agent-identity.js +1 -1
- package/dist/auth/clerk-sdk/generated.js +2 -1
- package/dist/auth/cloudflare-access.d.ts +3 -1
- package/dist/auth/cloudflare-access.js +22 -11
- package/dist/auth/downstream-oauth.js +195 -270
- package/dist/auth/downstream-revocation.js +1 -1
- package/dist/auth/oauth-grant.js +11 -0
- package/dist/auth/oauth-token-response.js +1 -1
- package/dist/auth/vercel-agent.js +2 -1
- package/dist/auth-elicitation.js +2 -2
- package/dist/branding.d.ts +1 -15
- package/dist/branding.js +2 -2
- package/dist/bytes.js +42 -0
- package/dist/call-admission-context.js +2 -5
- package/dist/call-admission.js +5 -0
- package/dist/call-recovery.js +0 -3
- package/dist/catalog-cache.js +26 -7
- package/dist/catalog-fingerprint.js +2 -18
- package/dist/catalog-revision.js +9 -0
- package/dist/catalog-service.js +51 -20
- package/dist/config-defaults.d.ts +1 -0
- package/dist/config-defaults.js +1 -0
- package/dist/config-schema.d.ts +1 -1
- package/dist/config-schema.js +6 -2
- package/dist/config.d.ts +41 -14
- package/dist/config.js +21 -71
- package/dist/connector-caller.js +1 -1
- package/dist/connectors/api-connector.js +4 -3
- package/dist/connectors/guarded-fetch.d.ts +0 -6
- package/dist/connectors/guarded-fetch.js +1 -11
- package/dist/connectors/remote-mcp.js +93 -69
- package/dist/connectors/resource-uri.js +0 -4
- package/dist/credentials.js +3 -0
- package/dist/d1.d.ts +0 -89
- package/dist/d1.js +0 -34
- package/dist/downstream-input.js +2 -2
- package/dist/execute.js +161 -116
- package/dist/executor-result.js +2 -1
- package/dist/executors/quickjs-runtime.js +2 -25
- package/dist/executors/quickjs.js +1 -1
- package/dist/guest-runtime.js +5 -3
- package/dist/guest-types.d.ts +5 -5
- package/dist/http/client.js +1 -1
- package/dist/http/errors.js +2 -2
- package/dist/http/json-schema.js +1 -1
- package/dist/http/node-transport.js +0 -1
- package/dist/http/oauth.js +1 -1
- package/dist/http/paging.js +6 -9
- package/dist/http/provider.js +1 -1
- package/dist/http/safety.js +1 -1
- package/dist/http/services.js +1 -8
- package/dist/http/tools.js +1 -7
- package/dist/http/vendor.js +7 -7
- package/dist/identity.js +2 -6
- package/dist/index.js +1 -2
- package/dist/invocation-auth.js +2 -1
- package/dist/invocation.js +8 -2
- package/dist/meta-tools.js +27 -27
- package/dist/native-tools.js +2 -1
- package/dist/oauth-handoff.js +2 -2
- package/dist/operation-vocabulary.js +1 -1
- package/dist/operator-record.d.ts +8 -2
- package/dist/operator-record.js +18 -50
- package/dist/operator-ui/contract.d.ts +4 -2
- package/dist/operator-ui/generated.js +3 -3
- package/dist/operator-ui/model.d.ts +7 -2
- package/dist/program-writes.js +3 -8
- package/dist/provider-call.js +0 -7
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/kit.js +1 -6
- package/dist/providers/_shared/google/service-account.js +1 -3
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/_shared/guide.js +3 -0
- package/dist/providers/_shared/json.js +12 -0
- package/dist/providers/_shared/rest/compact-index.js +1 -1
- package/dist/providers/_shared/rest/kit.js +1 -3
- package/dist/providers/_shared/rest/operation-index.js +1 -3
- package/dist/providers/_shared/rest/value-safety.js +26 -28
- package/dist/providers/breeze/api.js +2 -7
- package/dist/providers/breeze/index.js +4 -2
- package/dist/providers/breeze/tools.js +3 -4
- package/dist/providers/ccb/api.js +1 -6
- package/dist/providers/ccb/index.js +2 -1
- package/dist/providers/ccb/tools.js +1 -6
- package/dist/providers/cloudflare/api.js +2 -2
- package/dist/providers/cloudflare/index.js +2 -1
- package/dist/providers/cloudflare/safety.js +3 -3
- package/dist/providers/docs/tools.js +2 -10
- package/dist/providers/drive/tools.js +12 -25
- package/dist/providers/forms/tools.js +1 -9
- package/dist/providers/gmail/tools.js +5 -21
- package/dist/providers/overflow/api.js +1 -3
- package/dist/providers/overflow/index.js +2 -1
- package/dist/providers/overflow/tools.js +3 -7
- package/dist/providers/planning-center/api.js +2 -1
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/planning-center/lib/json-api.js +1 -9
- package/dist/providers/planning-center/tools.js +2 -1
- package/dist/providers/sheets/tools.js +1 -9
- package/dist/providers/slides/tools.js +4 -13
- package/dist/providers/stripe/index.js +2 -1
- package/dist/providers/stripe/safety.js +2 -2
- package/dist/providers/tithely/api.js +2 -7
- package/dist/providers/tithely/index.js +3 -2
- package/dist/providers/tithely/tools.js +7 -5
- package/dist/providers/vercel/api.js +1 -6
- package/dist/providers/vercel/lib/pin.js +1 -1
- package/dist/providers/vercel/safety.js +1 -1
- package/dist/providers/vercel/tools.js +2 -1
- package/dist/redaction-marker.js +2 -2
- package/dist/registry.d.ts +4 -1
- package/dist/registry.js +35 -22
- package/dist/request-principal.js +15 -2
- package/dist/request-state.js +2 -2
- package/dist/routes/access-tokens.js +1 -1
- package/dist/routes/activity-disclosure.js +1 -1
- package/dist/routes/connect.js +2 -2
- package/dist/routes/credentials.js +1 -1
- package/dist/routes/dispatch.js +10 -0
- package/dist/routes/mcp.js +23 -21
- package/dist/routes/oauth-management.js +1 -1
- package/dist/routes/operator.js +53 -7
- package/dist/routes/shared.d.ts +8 -8
- package/dist/routes/shared.js +22 -15
- package/dist/routes/ui-config.js +6 -6
- package/dist/routes/ui-data.js +2 -2
- package/dist/routes/ui.js +9 -4
- package/dist/runtime/call-admission.js +1 -1
- package/dist/runtime/call.js +1 -1
- package/dist/runtime/settlement.js +1 -1
- package/dist/runtime/shared-read.js +2 -15
- package/dist/sent-secrets.d.ts +14 -6
- package/dist/sent-secrets.js +231 -85
- package/dist/server.js +4 -4
- package/dist/skills.js +8 -35
- package/dist/sqlite.d.ts +0 -19
- package/dist/sqlite.js +2 -83
- package/dist/storage/keys.js +29 -124
- package/dist/storage/sql-native.js +14 -111
- package/dist/storage/sql.d.ts +1 -24
- package/dist/storage/sql.js +1 -12
- package/dist/tool-error.js +4 -0
- package/dist/types.d.ts +4 -3
- package/dist/ui.js +3 -0
- package/dist/url-safety.js +0 -4
- package/dist/usage-guide.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +1 -1
- package/documentation/README.md +6 -42
- package/documentation/architecture.md +190 -507
- package/documentation/auth.md +172 -58
- package/documentation/code-mode.md +98 -66
- package/documentation/deploying.md +19 -40
- package/documentation/integrating.md +8 -19
- package/documentation/meta-tools.md +23 -6
- package/documentation/migration-0.33.md +5 -5
- package/documentation/migration-0.34.md +2 -4
- package/documentation/migration-retired-layouts.md +74 -0
- package/documentation/operator-tests.md +19 -119
- package/documentation/operator-ui.md +46 -8
- package/documentation/provider-auth-migration.md +4 -10
- package/documentation/provider-migration-0.29.md +4 -15
- package/examples/worker/AGENTS.md +4 -6
- package/examples/worker/README.md +20 -115
- package/guides/connecting-agents.md +7 -30
- package/guides/how-it-works.md +2 -2
- package/guides/safety-and-trust.md +4 -1
- package/package.json +19 -19
- package/templates/node/README.md +9 -26
- package/templates/node/package.json +1 -1
- package/dist/auth/oauth-v2-migration.js +0 -135
- package/dist/provider-call-facts.d.ts +0 -115
- package/dist/provider-call.d.ts +0 -112
- package/dist/storage/d1-copy.js +0 -255
- package/dist/storage/sql-schema.d.ts +0 -6
- package/examples/worker/kv-to-d1.wrangler.jsonc +0 -26
- 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.
|
|
35
|
-
|
|
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
|
|
228
|
+
(`test/server.test.ts`).
|
|
230
229
|
|
|
231
230
|
### Modern request metadata and transport refusals
|
|
232
231
|
|
|
@@ -315,6 +314,28 @@ surface, and the endpoint's `readOnly`.
|
|
|
315
314
|
`test/execute.test.ts` asserts the parity directly, because a sandbox path that
|
|
316
315
|
quietly diverges is how generated code would mint a capability.
|
|
317
316
|
|
|
317
|
+
Catalog search retains private, stable arrays for each request's registry and
|
|
318
|
+
safety view, so repeated searches reuse the lexical index. Endpoint and connector
|
|
319
|
+
read-only policy, hidden tools, classification and caller/pool grants still
|
|
320
|
+
bound the visible corpus. Views are keyed by their source catalog array, so a
|
|
321
|
+
replacement read cannot inherit an older index; failed reads remain evicted.
|
|
322
|
+
Before reusing a dynamic catalog, the request checks an in-process revision fence,
|
|
323
|
+
without storage reads, decryption or hashing. Catalog invalidation (including
|
|
324
|
+
operator refresh and OAuth connect/disconnect/restart) advances a counter for the
|
|
325
|
+
registry's cache store; built-in vault set/clear advances a counter for that vault.
|
|
326
|
+
These weakly held counters contain no catalogs, credentials or caller identity;
|
|
327
|
+
catalogs and derived views remain request-local. A moved fence replaces the
|
|
328
|
+
retained read and its search views, and prevents joining an older registry flight.
|
|
329
|
+
Changes made through another process/isolate or directly in storage need not be
|
|
330
|
+
observed mid-request; the next request uses the durable generation and auth checks.
|
|
331
|
+
Custom vault mutations must also invalidate the registry's catalog.
|
|
332
|
+
Authored static catalogs remain independent of credential reads.
|
|
333
|
+
No snapshot or index is shared across requests. Search and describe clone only
|
|
334
|
+
selected definitions before returning schemas or annotations; `loadConnector()`
|
|
335
|
+
and native catalog listings still return independent copies. Observed output
|
|
336
|
+
schemas are resolved when rendering each page rather than retained in the index.
|
|
337
|
+
`test/catalog-service.test.ts` guards reuse and mutation isolation.
|
|
338
|
+
|
|
318
339
|
Program result paging shares `meta-tools.ts`'s direct-call stash envelope and
|
|
319
340
|
reader. `program-result.ts` owns executor-independent byte limits;
|
|
320
341
|
`execute.ts` issues bounded transfer cursors, and `guest-runtime.ts` reconstructs
|
|
@@ -336,8 +357,12 @@ capacity boundary, not tenant fairness, and one deployment serves one tenant.
|
|
|
336
357
|
`admission.requests.maxDurationMs` defaults to 300,000 ms and may be set to a
|
|
337
358
|
positive whole number up to 2,147,483,647 ms. Its clock starts when admission
|
|
338
359
|
grants a permit and covers authorization, tool execution, and response delivery.
|
|
339
|
-
|
|
340
|
-
|
|
360
|
+
A header-named `subscriptions/listen` candidate starts this clock before auth;
|
|
361
|
+
if classification requires a fallback permit, that queue wait shares the same
|
|
362
|
+
deadline. The owning request aborts its derived signal at that deadline, including
|
|
363
|
+
signals passed to admission waiters and connector calls, and closes a live
|
|
364
|
+
response. A timed-out candidate withdraws its fallback waiter before returning
|
|
365
|
+
504, so a later slot release cannot admit the ended request. If workerd ends the
|
|
341
366
|
request without running that timer or cancellation, the controller reclaims
|
|
342
367
|
only the expired permit's scalar record on the next request (or a queued
|
|
343
368
|
request's own timer). It never reads the old request's signal or touches its
|
|
@@ -356,6 +381,10 @@ endpoint admission, core counts streamed bytes before any SDK or native tool
|
|
|
356
381
|
body reader. A body exactly at the cap proceeds through normal protocol
|
|
357
382
|
validation. Past the cap, core stops reading, cancels both stream branches and
|
|
358
383
|
returns HTTP 413 with JSON-RPC code `-33006`, `"MCP request body too large."`.
|
|
384
|
+
The inspection reads through the first chunk crossing the cap; the clone's tee
|
|
385
|
+
may pull one further chunk. The request-local signal wrapper reuses the input
|
|
386
|
+
body explicitly, without an intermediate pipe that could pull additional chunks
|
|
387
|
+
ahead. Bytes already buffered by the caller or host are outside this read bound.
|
|
359
388
|
The response retains the request's existing admission lifetime and releases
|
|
360
389
|
the permit when delivery ends. Caller cancellation and the existing request
|
|
361
390
|
deadline also cancel stalled reads. `describeConfig().limits.requests.maxBodyBytes`
|
|
@@ -413,139 +442,24 @@ Only an omitted or `undefined` `callAdmission` selects a connector's default.
|
|
|
413
442
|
The shared schema rejects malformed defined policies at factory construction,
|
|
414
443
|
and the registry validates custom connector policies before registration.
|
|
415
444
|
Falsy values such as `false`, `null`, `0`, and `""` are construction errors
|
|
416
|
-
(INV-11), never a way to disable admission.
|
|
417
|
-
exercise these refusals and unchanged defaults in
|
|
418
|
-
`test/call-admission-options.test.ts`.
|
|
445
|
+
(INV-11), never a way to disable admission.
|
|
419
446
|
|
|
420
447
|
### Production Worker disconnects
|
|
421
448
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
client. A raw Worker stream established the runtime behavior; a custom auth
|
|
426
|
-
provider returning a synthetic streaming 401 exercised Connecta's real admission
|
|
427
|
-
wrapper. Each case had its own Connecta instance with one request permit and no
|
|
428
|
-
queue. Health checks and subsequent MCP requests confirmed the same isolate ID.
|
|
429
|
-
The [recorded observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-2026-09-25.json)
|
|
430
|
-
contain all twelve cases, including served flags and client-end timing.
|
|
431
|
-
|
|
432
|
-
| Response and flag | Raw stream abort / cancel | Connecta source abort / cancel | Admission after response; next request |
|
|
433
|
-
| ----------------------------------------------------- | ------------------------- | ------------------------------ | -------------------------------------- |
|
|
434
|
-
| Heartbeats, no flag | neither | neither | active 1; 503 |
|
|
435
|
-
| Idle with a pending timer, no flag | neither | neither | active 1; 503 |
|
|
436
|
-
| Heartbeats, request signal enabled | abort only | both | active 0; 200 |
|
|
437
|
-
| Idle with a pending timer, request signal enabled | abort only | both | active 0; 200 |
|
|
438
|
-
| One chunk, then no timer or I/O, either configuration | neither | neither | active 1; 503 |
|
|
439
|
-
|
|
440
|
-
The last row is a different failure. The client observed the response end
|
|
441
|
-
_before_ its planned disconnect, although the source never closed itself.
|
|
442
|
-
The runtime ended the response without running JavaScript cleanup. This does
|
|
443
|
-
not show a missing abort on a still-live request. The other cases remained open
|
|
444
|
-
until the client aborted. Admission was sampled 1.5 seconds later, followed by
|
|
445
|
-
an actual MCP request; this measures retained capacity, not an infinite leak.
|
|
446
|
-
|
|
447
|
-
Enable `enable_request_signal` when deploying on Workers: it let Connecta's
|
|
448
|
-
abort listener cancel the source and release admission for live responses in
|
|
449
|
-
this experiment. It does not solve runtime-ended streams. The admitted-request
|
|
450
|
-
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
|
|
451
452
|
deadline, even if the old request's timer never ran. This is capacity recovery,
|
|
452
453
|
not proof that workerd cancelled an old stream. A still-live request reaches
|
|
453
454
|
the same hard deadline and is aborted in its own context.
|
|
454
455
|
Cloudflare documents the flag in its
|
|
455
456
|
[Request API](https://developers.cloudflare.com/workers/runtime-apis/request/).
|
|
456
457
|
|
|
457
|
-
### Authentication release by collection on workerd
|
|
458
|
-
|
|
459
|
-
Measured on 2026-10-20 with workerd 1.20260722.1 (Miniflare 4.20260722.0),
|
|
460
|
-
compatibility date `2025-01-01`:
|
|
461
|
-
|
|
462
|
-
- `FinalizationRegistry` and `WeakRef` are undefined without
|
|
463
|
-
`enable_weak_ref`; the flag's default-on date is 2025-05-05.
|
|
464
|
-
- With `enable_weak_ref` and `enable_request_signal`, an object reachable only
|
|
465
|
-
through a fetch continuation is finalized once its invocation ends and
|
|
466
|
-
workerd cancels the fetch; while the invocation runs it is not. Pending
|
|
467
|
-
timers and listeners on the request's signal do not keep it.
|
|
468
|
-
- Strong values in a WeakMap keyed by a `Request` (even one the handler
|
|
469
|
-
created), properties set on a `Request`, and WeakMap values keyed by its
|
|
470
|
-
`Headers`, `ctx` or `env` stay reachable after the invocation ends. The
|
|
471
|
-
lifetime registry in `src/auth-admission.ts` therefore stores `WeakRef`s.
|
|
472
|
-
- No `gc()` is exposed. Allocating large arrays (32 × 1 MiB per request)
|
|
473
|
-
triggers collections, but the most recent unreachable object often survives
|
|
474
|
-
them; V8 in this build scans the native stack conservatively. The inspector's
|
|
475
|
-
precise `HeapProfiler.collectGarbage` freed it.
|
|
476
|
-
- In the vitest-pool-workers harness, an isolate that had already collected
|
|
477
|
-
one scenario's cancelled work sometimes kept a later scenario's cancelled
|
|
478
|
-
work reachable through 400 rounds of pressure; standalone Miniflare never
|
|
479
|
-
did. `test/auth-admission-binding.test.ts` gives each collection scenario a
|
|
480
|
-
fresh isolate and requires two of four sentinels collected before asserting
|
|
481
|
-
that live charges stayed counted.
|
|
482
|
-
|
|
483
|
-
A second production run on the same date tested the request bound at 2,000 ms.
|
|
484
|
-
All twelve raw-stream and MCP cases passed with both flag configurations. Live
|
|
485
|
-
MCP requests still rejected a competing request with 503 before the deadline;
|
|
486
|
-
the first MCP request after the deadline returned all eight tools with 200 in
|
|
487
|
-
the original isolate. Admission then reported zero active requests. Without the
|
|
488
|
-
flag, the source still recorded no abort or cancellation, so recovery did not
|
|
489
|
-
rely on runtime cleanup. The [recovery observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-recovered-2026-09-25.json)
|
|
490
|
-
also retain two inconclusive queue-handoff cases: concurrent client connections
|
|
491
|
-
reached different isolates and could not establish an abandoned queued waiter.
|
|
492
|
-
A [second trial using HTTP/2](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect-h2-2026-09-25.json)
|
|
493
|
-
repeated the twelve passing core cases and verified queued cancellation with
|
|
494
|
-
request signals enabled. Even on one HTTP/2 connection, the no-signal queue case
|
|
495
|
-
crossed isolates and remained inconclusive. The Node and workerd regression
|
|
496
|
-
suites cover expired waiters, orphaned handoffs, and identity-safe late release
|
|
497
|
-
deterministically.
|
|
498
|
-
|
|
499
|
-
The [historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/worker-disconnect.mjs)
|
|
500
|
-
and observations are retained in Git history, outside the current checkout and
|
|
501
|
-
npm package. The probe deployed a disposable Worker containing only synthetic
|
|
502
|
-
data, verified the served configuration, saved observations, and deleted the
|
|
503
|
-
Worker in `finally`. It used no `waitUntil`, which would change the lifetime
|
|
504
|
-
being measured. The production runtime build number was not exposed by the
|
|
505
|
-
deployment; these findings are tied to the date and flags above.
|
|
506
|
-
|
|
507
458
|
### Production Access bypass for agent endpoints
|
|
508
459
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
`ctx.access`. It can. The probe ran on 2026-10-15 against a disposable Worker on
|
|
513
|
-
its `workers.dev` hostname, with compatibility date `2026-09-01`, `cf`
|
|
514
|
-
1.0.0-beta.14, Wrangler 4.114.0, and curl 8.7.1 with `--path-as-is`, so raw
|
|
515
|
-
paths reached the edge unnormalized. The Worker-level application allowed only
|
|
516
|
-
a probe service token. Its unauthenticated refusal was therefore Access's 403
|
|
517
|
-
page rather than a login redirect. The [observations](https://github.com/zackbart/connecta/blob/main/scripts/probes/access-agent-bypass-2026-10-15.json)
|
|
518
|
-
record three identical rounds after each configuration change had produced
|
|
519
|
-
three consecutive matching responses.
|
|
520
|
-
|
|
521
|
-
- **Precedence.** A self-hosted application whose destination is
|
|
522
|
-
`{ "type": "public", "uri": "<host>/agent/*" }`, with one `bypass` policy
|
|
523
|
-
including `everyone`, overrides the Worker-level application on matching
|
|
524
|
-
paths only. Unauthenticated requests reach the Worker there. `/mcp`, `/`,
|
|
525
|
-
and every unmatched path still get the Access refusal. A service token is
|
|
526
|
-
still admitted on them with `ctx.access` from the Worker-level application.
|
|
527
|
-
This confirms Cloudflare's documented hierarchy, and that Worker-level
|
|
528
|
-
protection still covers the `workers.dev` hostname.
|
|
529
|
-
- **`ctx.access` on a bypassed request is present, not absent.** Its `aud` is
|
|
530
|
-
nonempty and is the _path_ application's AUD, not the Worker-level one.
|
|
531
|
-
`getIdentity()` resolved to `undefined` for every credential tested: none, a
|
|
532
|
-
valid service token, a `cta_`-style bearer token, and a forged
|
|
533
|
-
`Cf-Access-Jwt-Assertion`. A service token on a protected path also gave
|
|
534
|
-
`undefined` identity, carrying the Worker-level AUD. A human
|
|
535
|
-
`CF_Authorization` cookie on a bypassed path was not tested, so what
|
|
536
|
-
`getIdentity()` returns there is unknown. That is one more reason the agent
|
|
537
|
-
route ignores Access context rather than relying on its contents.
|
|
538
|
-
- **API shape.** A Worker destination is exclusive to one application. A
|
|
539
|
-
second application with `{ "type": "worker", "worker_id" }` plus `uri` or
|
|
540
|
-
`path` was refused with 409 `destination belongs to another application`. The
|
|
541
|
-
bypass must be hostname-and-path, stored with domain `<host>/agent/*`, and it
|
|
542
|
-
covers only that exact hostname.
|
|
543
|
-
- **Propagation.** Creating or deleting each application took 15–19 seconds to
|
|
544
|
-
settle across runs. While a deployment or application change was propagating,
|
|
545
|
-
the edge returned transient 500 (`error code: 1104`) and 404 (`error code:
|
|
546
|
-
1042` or `1043`) responses. The recorded run has one such 404, on the
|
|
547
|
-
Worker-level-only service-token control for `/agent/x`. Deleting the path
|
|
548
|
-
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.
|
|
549
463
|
|
|
550
464
|
| Raw request path | Access outcome | Worker pathname (+ search) |
|
|
551
465
|
| -------------------------- | -------------- | -------------------------- |
|
|
@@ -577,139 +491,32 @@ leading `//`, and treats `%2f` as data, not a separator. The effective Worker
|
|
|
577
491
|
pathname, `new URL(request.url).pathname`, is the raw request path with case,
|
|
578
492
|
escapes, and slashes untouched, except that dot segments arrive resolved:
|
|
579
493
|
`/mcp/../agent/x` arrives as `/agent/x`. Every path that
|
|
580
|
-
resolves outside `/agent/*`, including every `/mcp` form,
|
|
494
|
+
resolves outside `/agent/*`, including every `/mcp` form, remains protected. The
|
|
581
495
|
inverse is the risk: Access bypasses paths
|
|
582
496
|
that connecta must not treat as an agent pool. Connecta's router compares raw
|
|
583
497
|
pathnames without decoding, case folding, or slash collapsing, so an `/agent/`
|
|
584
|
-
route
|
|
585
|
-
`/agent/<pool>`, with `<pool>` byte-equal to a declared pool name. It
|
|
586
|
-
|
|
587
|
-
variants, and a leading `//`, all with the same 404. It
|
|
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
|
|
588
502
|
`ctx.access` entirely, since a bypassed request still carries an AUD.
|
|
589
503
|
[Agent endpoints](./auth.md#agent-endpoints) implement exactly this in
|
|
590
504
|
`src/routes/agent-path.ts`, and `test/agent-endpoints.test.ts` replays every
|
|
591
505
|
row of the table above. The hyphenated row stands for any two-part pool name
|
|
592
|
-
such as `<agent>-<user>`.
|
|
593
|
-
with placeholders after the run; the edge behavior for that path shape was
|
|
594
|
-
unaffected.
|
|
595
|
-
|
|
596
|
-
To configure the bypass, keep the Worker-level application described in
|
|
597
|
-
[Cloudflare Access on Workers](./auth.md#cloudflare-access-on-workers). Then,
|
|
598
|
-
for each hostname agents use (the `workers.dev` hostname, each Custom Domain,
|
|
599
|
-
or route host), add a self-hosted Access application. In the dashboard, set
|
|
600
|
-
domain to that hostname and path to `agent/*`, with one policy whose action is
|
|
601
|
-
**Bypass** and whose include rule is **Everyone**. Through the API, that is:
|
|
602
|
-
|
|
603
|
-
```json
|
|
604
|
-
{
|
|
605
|
-
"type": "self_hosted",
|
|
606
|
-
"name": "connecta agents",
|
|
607
|
-
"destinations": [{ "type": "public", "uri": "connecta.example.com/agent/*" }],
|
|
608
|
-
"policies": [{ "name": "agents bypass", "decision": "bypass", "include": [{ "everyone": {} }] }]
|
|
609
|
-
}
|
|
610
|
-
```
|
|
506
|
+
such as `<agent>-<user>`.
|
|
611
507
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
rely on a fixed wait after a change. Changes settled in 15–19 seconds, yet one
|
|
615
|
-
earlier round still saw a 403 on a bypassed path more than 30 seconds after
|
|
616
|
-
the change. Test only after the positive and negative controls agree together
|
|
617
|
-
for several consecutive rounds. The positive controls are bypassed paths
|
|
618
|
-
reaching the Worker with the path AUD and a service token reaching a protected
|
|
619
|
-
path with the Worker-level AUD; the negative controls are `/mcp` and `/`
|
|
620
|
-
returning the Access refusal. The current script requires five such rounds
|
|
621
|
-
after creating the bypass and three after other changes. The recorded run
|
|
622
|
-
predates this: after the bypass it converged only the positive `/agent/x`
|
|
623
|
-
control, then waited a fixed 90 seconds, and its negative controls were checked
|
|
624
|
-
in the recorded rounds rather than before them.
|
|
625
|
-
|
|
626
|
-
To repeat from a repository checkout, run
|
|
627
|
-
`node scripts/probes/access-agent-bypass.mjs --profile <name> [output.json]`.
|
|
628
|
-
`--profile` is required and has no default; every `cf` call passes it
|
|
629
|
-
explicitly. Name the profile for the account that should host the probe; it
|
|
630
|
-
needs
|
|
631
|
-
Access application, policy, and service-token writes, plus Workers script
|
|
632
|
-
writes. The script deploys and deletes the Worker with `cf`. `cf deploy` builds
|
|
633
|
-
through Wrangler 4.136.0 or newer, so the probe installs Wrangler 4.145.0 into
|
|
634
|
-
its temporary project. Wrangler needs no login. The script records only the
|
|
635
|
-
count of existing Access applications, then names everything it creates
|
|
636
|
-
`connecta-probe-<run>`. It force-deletes and verifies each resource in
|
|
637
|
-
`finally`, then checks that the non-probe application ids are unchanged.
|
|
638
|
-
Observations record statuses, outcome classes, pathnames, and which
|
|
639
|
-
application's AUD matched, compared by digest inside the Worker. They contain no
|
|
640
|
-
hostnames, account ids, tokens, cookies, or JWTs. `cf` delete commands prompt
|
|
641
|
-
for confirmation and exit 0 without deleting when stdin is not a terminal, so
|
|
642
|
-
the script passes `--force`. `cf deploy` printed `Deploy complete` and then
|
|
643
|
-
kept running, so the script stops it after that line. The recorded run deployed
|
|
644
|
-
with Wrangler 4.114.0 and used `cf` for Access. A standalone deploy and delete
|
|
645
|
-
verified the `cf` deploy and delete path. The full probe has not been rerun on
|
|
646
|
-
that path.
|
|
647
|
-
|
|
648
|
-
Every subprocess (`cf`, `curl`, `npm`) runs through one runner. It enforces a
|
|
649
|
-
deadline by killing the process group, keeps stdout and stderr in memory, and
|
|
650
|
-
returns only facts: success, exit code, timeout, HTTP status, numeric API
|
|
651
|
-
code, and an error slug from a fixed set (anything else is `unknown`). Commands
|
|
652
|
-
that return data are parsed inside the runner against schemas that copy only
|
|
653
|
-
whitelisted fields: ids, names, counts, booleans, and the Worker's `ctx.access`
|
|
654
|
-
summary. Credentials stay in a holder that prints as `[secret]`. An unexpected
|
|
655
|
-
shape, including `null` where a list belongs, is a failure, never an empty
|
|
656
|
-
result. Errors are fixed diagnostics plus those facts. Teardown deletes every
|
|
657
|
-
tracked id on its own. Then Access applications, service tokens, and Workers
|
|
658
|
-
each get their own bounded discovery-and-delete pass by name, for resources
|
|
659
|
-
whose create response was lost, so a failure in one family never skips
|
|
660
|
-
another. Each discovered resource is deleted independently. Access policies are
|
|
661
|
-
inline on their applications and go with them. A resource counts as gone only
|
|
662
|
-
when a successful lookup omits it, and the final inventory verifies each family
|
|
663
|
-
separately. Run
|
|
664
|
-
`node scripts/probes/access-agent-bypass.mjs --self-test` to check the runner
|
|
665
|
-
offline. It feeds canary secrets, suffixed slugs, malformed JSON, and a stalled
|
|
666
|
-
process tree through it. It also runs teardown against a fake `cf` whose
|
|
667
|
-
application listing always fails, after a lost token-create response, and
|
|
668
|
-
checks that the token is still found and deleted. It also checks that a Worker
|
|
669
|
-
that refuses deletion does not block another Worker's deletion. It runs no real
|
|
670
|
-
`cf`, `curl`, or network command. This
|
|
671
|
-
hardening and the joint convergence are verified offline only and have not run
|
|
672
|
-
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).
|
|
673
510
|
|
|
674
511
|
### Alchemy Worker deployment
|
|
675
512
|
|
|
676
|
-
Two throwaway deployments on 2026-10-20 verified the Worker example's
|
|
677
|
-
Alchemy path with alchemy 2.0.0-beta.83, effect 4.0.5, and Alchemy local
|
|
678
|
-
state, then deleted every resource and confirmed by re-listing. Alchemy
|
|
679
|
-
uploads the full Worker with `PUT /workers/scripts/<name>`, the endpoint an
|
|
680
|
-
earlier full deploy had been suspected of losing `ctx.access` through.
|
|
681
|
-
|
|
682
|
-
The first deployment ran a near copy of the stack with a temporary diagnostic
|
|
683
|
-
route. With an Access service token in a non-identity policy, the Worker saw
|
|
684
|
-
`ctx.access` carrying the Worker-level application's AUD and no identity, as
|
|
685
|
-
expected for a service token. D1, the Worker Loader, R2, and secret bindings
|
|
686
|
-
worked; `/agent/*` reached connecta through the bypass; `/ui/api/config`
|
|
687
|
-
returned 403 `Cloudflare Access human identity required`; and
|
|
688
|
-
`connecta doctor` passed with a `cta_` token issued inside the Worker. A code
|
|
689
|
-
update through `alchemy deploy` kept the script tag, `ctx.access`, and D1 rows.
|
|
690
|
-
Three consecutive probe rounds agreed.
|
|
691
|
-
|
|
692
|
-
The second deployment ran the shipped `alchemy.run.ts` from the packed package
|
|
693
|
-
in a separate project on Node 26, changing only resource names and the Access policy
|
|
694
|
-
(a service token). The edge checks in the example's "Verify after deploying"
|
|
695
|
-
held for three or more rounds after each deploy. `alchemy destroy` deleted the
|
|
696
|
-
Worker and both Access applications and orphaned the retained D1 database; the
|
|
697
|
-
next deploy adopted it by name with its rows. After deleting local state, a
|
|
698
|
-
deploy reused the Worker with the same script tag, and `applicationId` took
|
|
699
|
-
both Access applications over with unchanged AUDs and no duplicates. Removing
|
|
700
|
-
`applicationId` afterwards planned an in-place update, and a further deploy
|
|
701
|
-
planned no changes. The account API token needed only Workers Scripts Write,
|
|
702
|
-
D1 Write, Access: Apps and Policies Write, and Access: Organizations, Identity
|
|
703
|
-
Providers, and Groups Read.
|
|
704
|
-
|
|
705
513
|
Four findings shape the operator rules. Alchemy local state under
|
|
706
514
|
`.alchemy/state/` records secret binding values in plaintext. Alchemy sends no
|
|
707
515
|
`keep_bindings`, so each deploy deletes secrets set outside the stack.
|
|
708
516
|
`alchemy dev` does not provide `ctx.access` to a plain fetch Worker, so local
|
|
709
517
|
development stays on `wrangler dev` and its `access.dev` identity. D1 needs
|
|
710
518
|
`RemovalPolicy.retain()` so `alchemy destroy` cannot delete a deployment's
|
|
711
|
-
state.
|
|
712
|
-
taking over a Worker first deployed with Wrangler.
|
|
519
|
+
state.
|
|
713
520
|
|
|
714
521
|
npm strips `.gitignore` from the package, so the copied Worker example's
|
|
715
522
|
ignore rules ship as `examples/worker/gitignore`, renamed by the README's copy
|
|
@@ -730,15 +537,8 @@ activity table beside it. `src/storage/sql.ts` owns the Promise edges.
|
|
|
730
537
|
Each store creates its tables on first use. `memoryStorage()` is the default
|
|
731
538
|
and the test double. Workers KV and the 0.28 JSON file store are gone: KV is
|
|
732
539
|
eventually consistent and cannot compare-and-set, and the file store rewrote
|
|
733
|
-
its whole file on every write.
|
|
734
|
-
|
|
735
|
-
entries verbatim, because physical keys did not change, and both keep any key
|
|
736
|
-
the target already holds. `copyKvToD1` reports counts per key family
|
|
737
|
-
(`familyOfKey` in `src/storage/keys.ts`), never a key. It resumes through a
|
|
738
|
-
source-bound, atomically claimed token, because Workers KV's own list cursor
|
|
739
|
-
can spell a key. Copy under maintenance after KV stabilizes, verify hashes
|
|
740
|
-
and expiries, and mark cutover before reopening traffic; the permanent D1
|
|
741
|
-
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).
|
|
742
542
|
|
|
743
543
|
`KVStorage` is `get`/`set`/`delete`/`list(prefix)`/`compareAndSet`, all
|
|
744
544
|
required, and `createConnecta` refuses storage missing one (INV-11).
|
|
@@ -762,6 +562,11 @@ read-then-write fallback: agent-token slot swaps and
|
|
|
762
562
|
adoption, OAuth handoff ownership, single-use connect links, every downstream
|
|
763
563
|
OAuth grant write and consent claim, the result stash, and initialization of the cursor signing key.
|
|
764
564
|
|
|
565
|
+
`set()` and mutating `compareAndSet()` statements first reclaim at most 16
|
|
566
|
+
expired SQL rows per call. The CAS sweep runs as a separate statement; its
|
|
567
|
+
affected-row count cannot change the comparison's boolean result. A
|
|
568
|
+
`compareAndSet(key, null, null)` only checks absence and does not sweep.
|
|
569
|
+
|
|
765
570
|
Every key is built in `src/storage/keys.ts`, which lists each family with its
|
|
766
571
|
scope, version, codec, and TTL policy. Deployed-consumer persistence journeys
|
|
767
572
|
check actual owner isolation in the physical database; key probes retain NUL
|
|
@@ -773,12 +578,10 @@ Keys and list prefixes must not contain U+0000 (NUL). D1, SQLite, and memory
|
|
|
773
578
|
storage reject them with `TypeError` before accessing storage: Node 22's
|
|
774
579
|
`node:sqlite` truncates TEXT results at NUL. Builders reject NUL in unencoded
|
|
775
580
|
components.
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
the file (INV-6). Database failures report only a fixed import step and an
|
|
781
|
-
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).
|
|
782
585
|
|
|
783
586
|
Remote MCP catalogs use the SDK `ResponseCacheStore` over this same SQL KV.
|
|
784
587
|
The `response-cache:v1:` family binds connector id, a hash of its configured
|
|
@@ -868,6 +671,16 @@ that charge until its deadline. Booking retries recognize their own reservation
|
|
|
868
671
|
ID and reuse its stored deadline, including a final read after an ambiguous
|
|
869
672
|
last CAS. A lost swap or transient storage error backs off and re-reads, up to
|
|
870
673
|
32 attempts. A full ledger or unconfirmed exhausted booking returns no result id.
|
|
674
|
+
The entire stash operation, including reservation reads, has a 45-second
|
|
675
|
+
outer budget. Invocation result processing shares the call's remaining deadline
|
|
676
|
+
and request cancellation, so shorter call or request limits stop stash waits
|
|
677
|
+
sooner. Program paging runs in that same scope; a stalled storage promise cannot
|
|
678
|
+
retain its executor permit after cancellation. Late storage responses start no
|
|
679
|
+
further stash work.
|
|
680
|
+
Final program-return stashing composes through its registered Effect program
|
|
681
|
+
after the executor scope closes, under the caller's cancellation rather than
|
|
682
|
+
the closed run's signal. Cancellation settles the handler while preserving
|
|
683
|
+
completed-write accounting; it cannot resume stashing after a held read returns.
|
|
871
684
|
|
|
872
685
|
Caller time only nominates charges for pruning. Before discarding one, the
|
|
873
686
|
ledger writes a probe with that charge's absolute deadline and reads it back.
|
|
@@ -949,14 +762,37 @@ Basic form components so a raw echo is covered too. Public client IDs remain
|
|
|
949
762
|
visible. Redaction replaces these values and their auth prefixes, mixed JSON
|
|
950
763
|
escapes, URL encodings, and base64/base64url forms with `[redacted]`.
|
|
951
764
|
|
|
952
|
-
Every redaction
|
|
765
|
+
Every redaction builds one text projection of the whole value: string
|
|
953
766
|
and number leaves in serialization order, nested JSON strings parsed into their
|
|
954
767
|
own leaves, and errors' own fields (non-enumerable diagnostics and causes
|
|
955
|
-
included). The joined projection is matched
|
|
768
|
+
included). The joined projection is matched raw and through each JSON
|
|
956
769
|
decoding layer, so a form split across adjacent leaves is found; each match is
|
|
957
770
|
replaced only in the leaves it touches, so public runs, links, annotations and
|
|
958
|
-
container shapes stay.
|
|
959
|
-
|
|
771
|
+
container shapes stay. Touched numbers are first checked together with non-number
|
|
772
|
+
pieces already replaced, so a number stays numeric when replacing a neighbouring
|
|
773
|
+
string breaks the match. Every replacement is then checked in the lane as it
|
|
774
|
+
will be returned: partial string replacements, whole numbers, typed binary,
|
|
775
|
+
decoded binary, whole-field private matches and unchecked runs included. A
|
|
776
|
+
withheld error number contributes empty text, matching its `undefined` field on
|
|
777
|
+
the error copy. An unregistered error name is one whole-withheld group, even
|
|
778
|
+
when its text was parsed into several JSON leaves: any changed leaf collapses
|
|
779
|
+
that group to one placeholder before settling. JSON strings withheld for a
|
|
780
|
+
match spanning tokens, and scalars withheld while walking, contribute their
|
|
781
|
+
actual placeholders instead of their discarded contents.
|
|
782
|
+
A newly formed match reaching a kept piece withholds that entire part, and the
|
|
783
|
+
lane is checked again until nothing changes. This includes matches formed from
|
|
784
|
+
`[redacted]` text and a kept neighbour, within one string or across leaves;
|
|
785
|
+
a span wholly inside a placeholder remains the placeholder. Each changing
|
|
786
|
+
round withholds at least one more part, so there are at most as many changing
|
|
787
|
+
rounds as parts. Every round, including its bookkeeping, shares the call's work
|
|
788
|
+
budget. An unaffordable leaf or seam check withholds every participating part,
|
|
789
|
+
including neighbours untouched by the original scan and any originally touched
|
|
790
|
+
numbers awaiting retention. A marker that could not itself be scanned still
|
|
791
|
+
participates in its seam windows. Unchanged lanes pay for no recheck.
|
|
792
|
+
Joined and leaf-by-leaf matching follow the same rule; large lanes recheck
|
|
793
|
+
changed parts and the seams touching them without rereading unchanged leaf
|
|
794
|
+
interiors. Bounded lanes and deep escapes withhold changed numbers whole.
|
|
795
|
+
The walk reads every field once, from its descriptor, and returns what it checked. A caller's container stands only as verified plain
|
|
960
796
|
data: prototype `Object.prototype`, `Array.prototype` or `null`, no `toJSON` on
|
|
961
797
|
its chain, no symbol-keyed field, every field a data property it read (an
|
|
962
798
|
array's holes read through its prototypes), and nothing in it changed. Anything
|
|
@@ -969,8 +805,12 @@ as plain data; an array that is not plain as a plain array of its indices; a
|
|
|
969
805
|
`String`, `Number` or `Boolean` wrapper as its primitive; a `toJSON` result,
|
|
970
806
|
called once and checked; a function or symbol as the placeholder; a typed array,
|
|
971
807
|
`DataView` or `ArrayBuffer` as a fresh `Uint8Array` of its bytes, which are
|
|
972
|
-
checked as bytes
|
|
973
|
-
|
|
808
|
+
checked as bytes on their own. Its returned byte numbers join neighbouring
|
|
809
|
+
leaves directly in one projection; non-byte typed arrays' original element
|
|
810
|
+
numbers substitute for those bytes in a separate projection with the same
|
|
811
|
+
neighbours. Neither numeric view interrupts the other. Each binary is one
|
|
812
|
+
whole-withheld part, so a match immediately exposes its placeholder to the lane
|
|
813
|
+
check. The type table is in [auth.md](./auth.md). An own accessor is
|
|
974
814
|
never invoked: an object's copy leaves it out, an array index holds the
|
|
975
815
|
placeholder. A change copies the containers on its path on first write, so a
|
|
976
816
|
caller's value is never mutated. An array's non-index fields are never read:
|
|
@@ -997,10 +837,18 @@ Connecta mints and authenticates are kept whole: they hold only sealed state or
|
|
|
997
837
|
Connecta's own facts. Serialized JSON whose keys, syntax and leaves together
|
|
998
838
|
hold a run of a form that no single leaf holds is withheld whole.
|
|
999
839
|
|
|
1000
|
-
|
|
840
|
+
Settling covers this joined projection, including whole replacement markers;
|
|
841
|
+
object keys, JSON punctuation and fixed framing do not join that lane. Keys
|
|
842
|
+
are checked alone, and serialized token-spanning matches follow the separate
|
|
843
|
+
whole-JSON rule above.
|
|
844
|
+
|
|
845
|
+
The guarantee for these checked text views and projections: **no caller-visible
|
|
846
|
+
text holds a registered form whole, nor any
|
|
1001
847
|
run of K = 8 consecutive UTF-16 units of a registered form of a value at least K
|
|
1002
848
|
long, in the raw text or any JSON-decoded layer; text cut where the cut can end
|
|
1003
|
-
inside a form loses that unfinished tail, however short.**
|
|
849
|
+
inside a form loses that unfinished tail, however short.** Runs formed from
|
|
850
|
+
placeholder text and kept text or numbers are covered too; the placeholder
|
|
851
|
+
itself is reserved replacement text. Registered forms are
|
|
1004
852
|
the raw value, its URL/form encodings (either percent hex case), base64 and
|
|
1005
853
|
base64url, and the `Bearer `/`token ` framings. The framing words are public, so
|
|
1006
854
|
no indexed run starts inside them; a run reaching K units of the value is
|
|
@@ -1022,7 +870,12 @@ result and errors, the invocation's raw reply, executor outcomes and logs,
|
|
|
1022
870
|
emits, and resource and skill reads. The agent boundary (`redactAgentOutput`,
|
|
1023
871
|
every meta-tool exit and the MCP route) repeats every check except short
|
|
1024
872
|
private values, which would rewrite Connecta's own ids, counts and addresses;
|
|
1025
|
-
payload passes already removed them.
|
|
873
|
+
payload passes already removed them. Each `execute_code` response crosses one
|
|
874
|
+
operation-boundary envelope pass after write accounting, including success,
|
|
875
|
+
failure, budget refusal, cancellation and oversized-code intake refusal. The
|
|
876
|
+
pass includes emits and diagnostics. Connecta attaches its own integer
|
|
877
|
+
`hostCalls` counters afterward. The MCP route still checks the serialized HTTP
|
|
878
|
+
response separately. Identifiers sent beside a credential — a
|
|
1026
879
|
confidential client's ID, a Basic username beside a password of at least eight
|
|
1027
880
|
characters, and credential-slot fields named as identifiers (`…Id`, `user`,
|
|
1028
881
|
`username`, `login`, `email`, `public…`) — are matched whole only: consent URLs
|
|
@@ -1148,6 +1001,14 @@ The matcher is cached until its set changes; the empty set has a fast path.
|
|
|
1148
1001
|
including errors, and every host-to-guest value or rejection passes through
|
|
1149
1002
|
it. Both MCP transports also pass their serialized response through it, covering
|
|
1150
1003
|
JSON-RPC errors, HTTP diagnostics, structured content, and paging responses.
|
|
1004
|
+
After fully reading an SDK response body, an idle request set (no registered
|
|
1005
|
+
forms or unregistered values) returns the original text without parsing or
|
|
1006
|
+
reserializing it, so clients receive exactly the bytes the SDK serialized. Only
|
|
1007
|
+
an active set reparses the body, which also canonicalizes numbers a handler
|
|
1008
|
+
emitted through `JSON.rawJSON` (an out-of-range value becomes `null`, digits past
|
|
1009
|
+
double precision are rounded); clients must not rely on either behaviour. A set
|
|
1010
|
+
activated before the read completes still applies structured redaction, with
|
|
1011
|
+
the text fallback for non-JSON diagnostics.
|
|
1151
1012
|
Per-call redaction remains before diagnostic truncation, paging, and emits. Paging stores only request-redacted text before encoding
|
|
1152
1013
|
chunks, so a later request needs no original credentials. Programs retain the
|
|
1153
1014
|
request set only for the run so later outputs cannot reconstruct an echo.
|
|
@@ -1190,9 +1051,8 @@ parse as text, so connecta reads downstream bodies as bytes
|
|
|
1190
1051
|
(`src/byte-read-response.ts`), `ctx.oauth.fetch()` answers included. The
|
|
1191
1052
|
operator page may show catalog metadata the operator loaded (descriptions,
|
|
1192
1053
|
schemas), but withholds a tool name outside the grammar as it does in records.
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
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,
|
|
1196
1056
|
Google's uncertain-write wording, and oversized-response mapping; see
|
|
1197
1057
|
[Read failure records](./operating.md#read-failure-records) for their operator
|
|
1198
1058
|
meaning. These facts do not change agent error fields or write outcomes.
|
|
@@ -1363,9 +1223,7 @@ acknowledgement, then reconciles vendor state with exactly one physical send.
|
|
|
1363
1223
|
Focused provider suites retain MIME/draft restrictions, revision conflicts,
|
|
1364
1224
|
CCB routing, Planning Center product exclusions and deployment pins. Native
|
|
1365
1225
|
HTTP framing, cursor and request-lifetime fault probes remain in the HTTP suites.
|
|
1366
|
-
|
|
1367
|
-
and unused paging strategy tests are retired under #1072. Provider workflows
|
|
1368
|
-
belong in the existing shared outcomes; a new tool requires no failure-matrix row.
|
|
1226
|
+
Provider workflows belong in the existing shared outcomes.
|
|
1369
1227
|
No legacy REST tool adapter remains.
|
|
1370
1228
|
Large documents opt into generator options that drop restated operation ids
|
|
1371
1229
|
and plain path parameters and cap one operation's details (`opBudget`).
|
|
@@ -1639,8 +1497,7 @@ path, or fields a schema leaves undeclared; such paths still apply at
|
|
|
1639
1497
|
runtime).
|
|
1640
1498
|
|
|
1641
1499
|
`test/fixtures/value-safety.ts` is the shared harness each vendor's
|
|
1642
|
-
`provider.safety.node.test.ts` runs; `reviewProblems` is pure
|
|
1643
|
-
`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
|
|
1644
1501
|
on candidates from another pin, format, or option set; an unreviewed
|
|
1645
1502
|
candidate; a verdict for an operation the index lacks; a table path the stamp
|
|
1646
1503
|
lacks (unresolved, or edited since `providers:spec`); a flagged field no path,
|
|
@@ -1668,12 +1525,6 @@ and reports only added, removed and changed counts in the caller's partition.
|
|
|
1668
1525
|
|
|
1669
1526
|
#### Behavioral evidence (contributor)
|
|
1670
1527
|
|
|
1671
|
-
The characterization snapshot engine, provider JSON snapshots, source scheduler
|
|
1672
|
-
and its self-tests are retired under
|
|
1673
|
-
[#1072](https://github.com/zackbart/connecta/issues/1072). Provider changes no
|
|
1674
|
-
longer require golden enrollment, snapshot updates, byte equivalence or nightly
|
|
1675
|
-
capture determinism. Production invariants remain unchanged.
|
|
1676
|
-
|
|
1677
1528
|
Use the existing stateful fixture vendors and published-edge deployment adapters:
|
|
1678
1529
|
|
|
1679
1530
|
- `pnpm run eval:outcomes` drives representative joins, retained paging, endpoint
|
|
@@ -1701,14 +1552,6 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
|
|
|
1701
1552
|
returned private values and credential echoes; positive record facts must
|
|
1702
1553
|
still arrive. An explicitly writable pool also exercises reopening a
|
|
1703
1554
|
read-only deployment without bypassing connector policy.
|
|
1704
|
-
- `test/cancellation-journey.node.test.ts` observes a queued client's departure
|
|
1705
|
-
without another vendor send, and an active client's departure after the
|
|
1706
|
-
vendor commits. The vendor must see its held response socket close before
|
|
1707
|
-
releasing it; a later MCP read recovers the committed intent exactly once.
|
|
1708
|
-
`test/cancellation-faults.test.ts` keeps signal-less token-resolution,
|
|
1709
|
-
warmed-credential digest, stalled body-cleanup and abort/grant races on Node
|
|
1710
|
-
and workerd. These replace the universal Promise-method/seam/owner matrix
|
|
1711
|
-
and its enrollment self-test, plus migration-only runtime wrapper checks.
|
|
1712
1555
|
- `test/browser/operator-flows.spec.ts` serves the actual operator shell and
|
|
1713
1556
|
routes over TCP with file-backed SQLite. Sign-in, Settings provenance, OAuth
|
|
1714
1557
|
connect/disconnect and credential save/test/replace/remove survive closing and
|
|
@@ -1742,17 +1585,10 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
|
|
|
1742
1585
|
after HTML, malformed JSON and oversized acknowledgements with one physical
|
|
1743
1586
|
send. It distinguishes Stripe's reviewed pre-execution 401 refusal, and checks
|
|
1744
1587
|
read failure facts, secrecy and recovery through direct and QuickJS calls.
|
|
1745
|
-
`test/
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
`test/request-secret-redaction.test.ts` and `test/catalog-secret-redaction.test.ts`
|
|
1750
|
-
cover secrecy before projection, truncation, caching, paging and diagnostics.
|
|
1751
|
-
- `test/write-evidence.node.test.ts`, `test/remote-mcp-attempts.node.test.ts` and
|
|
1752
|
-
`test/oauth-credential-attempts.node.test.ts` inspect physical socket sends.
|
|
1753
|
-
Native transport parity, OAuth and write-provenance suites retain their
|
|
1754
|
-
workerd coverage. The shared throwaway RSA key in `test/fixtures/rsa.ts` is
|
|
1755
|
-
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.
|
|
1756
1592
|
- `test/executor-requests.test.ts` runs overlapping `execute_code` programs
|
|
1757
1593
|
as separate workerd requests through the real Worker Loader executor, with
|
|
1758
1594
|
real TCP host calls that read their own request's signal. Queued programs
|
|
@@ -1767,23 +1603,21 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
|
|
|
1767
1603
|
`test/request-context.node.test.ts` pins the same wake order and async context
|
|
1768
1604
|
on Node for executor and call admission and QuickJS host calls, and gives back
|
|
1769
1605
|
grants that shutdown overtook.
|
|
1770
|
-
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
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.
|
|
1774
1609
|
|
|
1775
1610
|
The remaining probes defend distinct reachable failures: auth admission and
|
|
1776
1611
|
native scheduler ownership, grant/callback fencing and refresh coordination,
|
|
1777
1612
|
SDK replay/redirect/SSE attempts, nested write provenance and refusal evidence,
|
|
1778
1613
|
unawaited program drain and failed-program accounting, catalog drift and paging
|
|
1779
1614
|
revocation, and hostile serializers, structural echoes, truncation and persisted
|
|
1780
|
-
pages.
|
|
1781
|
-
post-401 result/error-shape tables are retired. Protocol claims link executable
|
|
1615
|
+
pages. Protocol claims link executable
|
|
1782
1616
|
evidence files in `spec/coverage.json`; the invariant contracts above are defended
|
|
1783
1617
|
by the actual behavior assertions in these journeys and focused probes.
|
|
1784
1618
|
The required CI gate includes build/declarations, Node and Workers probes,
|
|
1785
1619
|
outcomes, browser flows, installed consumers and Docker, audits and real Eve
|
|
1786
|
-
compatibility.
|
|
1620
|
+
compatibility.
|
|
1787
1621
|
|
|
1788
1622
|
Add a compact fault journey only for a meaningful outcome those suites cannot
|
|
1789
1623
|
observe. Reuse their fixtures and adapters; do not reintroduce a snapshot
|
|
@@ -1811,8 +1645,7 @@ behind the same redirect wrapper and a same-origin guard.
|
|
|
1811
1645
|
client replacement, transport confinement and secrecy on Node, and a real
|
|
1812
1646
|
service binding on workerd.
|
|
1813
1647
|
`test/google-service-account-token.test.ts` exercises signing/cache behavior on
|
|
1814
|
-
Node
|
|
1815
|
-
real-socket token/MCP requests, invalidation and one physical attempt per write.
|
|
1648
|
+
Node.
|
|
1816
1649
|
Operator setup lives in [Google hosted MCP](./auth.md#google-service-account-tokens-and-hosted-mcp).
|
|
1817
1650
|
|
|
1818
1651
|
`createConnecta` takes closed typed `ui`, `vault`, and `activity`
|
|
@@ -2093,10 +1926,8 @@ request fixture and native-output suites supply runtime parity evidence.
|
|
|
2093
1926
|
|
|
2094
1927
|
### Native SQL storage (contributor)
|
|
2095
1928
|
|
|
2096
|
-
`src/storage/sql-native.ts` composes KV, activity schema upgrades, retention
|
|
2097
|
-
|
|
2098
|
-
Workers KV copy, source-bound resume claims, verification, cutover guard, and
|
|
2099
|
-
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;
|
|
2100
1931
|
custom Promise SQL drivers retain the fallback lift.
|
|
2101
1932
|
|
|
2102
1933
|
`src/storage/sql-driver.ts` constructs a public Effect 4.0.5 `SqlClient`
|
|
@@ -2115,17 +1946,8 @@ is the existing exact `effect` pin. Connecta does not adopt `@effect/sql-d1`:
|
|
|
2115
1946
|
its 4.0.5 batch API drops metadata and its binding contract is wider than
|
|
2116
1947
|
Connecta's published structural interface.
|
|
2117
1948
|
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
read-only bundle artifacts: `/d1` grew from 62,516 to 88,305 bytes gzip
|
|
2121
|
-
(+25,789); the Worker example grew from 729,847 to 756,375 (+26,528).
|
|
2122
|
-
Final typed-driver failure normalization measures 88,319 and 756,377 bytes
|
|
2123
|
-
on that same official runtime. Executing real services and statement operations requires those runtime
|
|
2124
|
-
semantics; the earlier identity-compiler facade did not provide them. The
|
|
2125
|
-
explicit allowance changes only `/d1`'s cap from 62,542 to 90,000 bytes and
|
|
2126
|
-
the Worker example's cap from 743,000 to 760,000. Historical baselines and
|
|
2127
|
-
all other caps stay fixed. Final integration must remeasure the combined
|
|
2128
|
-
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).
|
|
2129
1951
|
|
|
2130
1952
|
The platform connection capability contains no request state. D1 prepares in
|
|
2131
1953
|
the current request and retains no prepared handles or promises. SQLite keeps
|
|
@@ -2152,14 +1974,11 @@ value, matching that fallback. Interruption remains interruption. The lookup
|
|
|
2152
1974
|
module imports no SQL or platform implementation and does not change the
|
|
2153
1975
|
published storage interfaces.
|
|
2154
1976
|
|
|
2155
|
-
|
|
2156
|
-
SQLite import remain native synchronous leaves because `importStateFile` is a
|
|
2157
|
-
published synchronous API. `openSqlite` retains file modes, WAL, busy timeout,
|
|
1977
|
+
`openSqlite` retains file modes, WAL, busy timeout,
|
|
2158
1978
|
and database ownership. `test/sql-effect-contract.ts` retains affected-row,
|
|
2159
1979
|
50-claim, typed-failure, cancellation, and tracing probes on SQLite and native
|
|
2160
|
-
D1.
|
|
2161
|
-
|
|
2162
|
-
activity upgrades, and copy recovery.
|
|
1980
|
+
D1. The existing SQL contracts prove rollback, database-clock TTL, encodings,
|
|
1981
|
+
and activity upgrades.
|
|
2163
1982
|
|
|
2164
1983
|
### Internal provider HTTP failures (contributor)
|
|
2165
1984
|
|
|
@@ -2200,7 +2019,7 @@ Stripe declares HTTP 409 as a conflict, alongside its fact-driven 429
|
|
|
2200
2019
|
classification to an oversized 424 reply, preserving the dependency-failure
|
|
2201
2020
|
meaning when the bounded body cannot be read. The original ceiling diagnostic
|
|
2202
2021
|
and answered status remain; the core still decides write outcomes. It does not
|
|
2203
|
-
apply to 401 or
|
|
2022
|
+
apply to 401 or change write-outcome settlement. A fixed read-phase fact distinguishes failed body
|
|
2204
2023
|
reads from malformed JSON only for vendors declaring that wording; raw
|
|
2205
2024
|
exceptions never enter diagnostics. Other providers retain their mapping and
|
|
2206
2025
|
wording.
|
|
@@ -2217,14 +2036,13 @@ The mapping records each request's own served provenance and raw 401 facts.
|
|
|
2217
2036
|
reservation's answer even for a read-classified tool issuing a physical write.
|
|
2218
2037
|
An accepted write's blocked retry retains its original HTTP status; a slot
|
|
2219
2038
|
occupied by an auxiliary read instead names the separate write step needed.
|
|
2220
|
-
Structured resend advice reaches output only when the core permits it.
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
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
|
|
2224
2043
|
transport suites exercise these contracts through invocation, physical sockets
|
|
2225
2044
|
and request closure. `http-kit.test.ts` retains focused numeric/text fact,
|
|
2226
|
-
request-equivalence and bulk-capacity fault probes.
|
|
2227
|
-
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
|
|
2228
2046
|
explicit provider subpaths; it stays off the root import graph and published
|
|
2229
2047
|
declarations expose Promises.
|
|
2230
2048
|
|
|
@@ -2235,8 +2053,7 @@ declarations expose Promises.
|
|
|
2235
2053
|
are Promises. A connector is called with `Effect.tryPromise`, never asked to
|
|
2236
2054
|
return an Effect.
|
|
2237
2055
|
- **Inbound authorization.** `authorize` in `src/routes/shared.ts` ships in
|
|
2238
|
-
the declarations and in the `./activity` bundle
|
|
2239
|
-
gzip there for nothing a caller could see.
|
|
2056
|
+
the declarations and in the `./activity` bundle.
|
|
2240
2057
|
- **Pure synchronous code.** Ranking, schema rendering, classification,
|
|
2241
2058
|
`validateToolInput`, the host-call budget, the emit collector, failure
|
|
2242
2059
|
framing, and result shaping. The microtask scheduler never yields to I/O, so
|
|
@@ -2996,17 +2813,9 @@ including structural lookalikes and genuine router errors, retain their identity
|
|
|
2996
2813
|
|
|
2997
2814
|
Four public `effect/http/*` modules are allowed only in the private dispatcher;
|
|
2998
2815
|
all other root-purity and optional-implementation boundaries remain enforced.
|
|
2999
|
-
Direct public module imports avoid the area's barrel cost.
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
repository bundle options). The barrel alternative measures 423,348/787,614.
|
|
3003
|
-
The parent accepted this measured native-routing cost with a Worker cap of
|
|
3004
|
-
785,000 bytes, including room for separately measured native-auth and reviewed
|
|
3005
|
-
host/transport additions. Root and all other caps and historical baselines are
|
|
3006
|
-
unchanged by D3. Separate-input costs are not a combined gzip measurement;
|
|
3007
|
-
the integrated deployment must be measured again.
|
|
3008
|
-
|
|
3009
|
-
`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:
|
|
3010
2819
|
raw prefixes and malformed suffixes, extension methods, CIMD before auth/UI/body
|
|
3011
2820
|
reads, Response/error identity, unmatched fallthrough, concurrent request
|
|
3012
2821
|
isolation, and body-held permits through end/failure/cancel/abort/abandonment.
|
|
@@ -3018,85 +2827,20 @@ Root purity, declaration and bundle checks own import boundaries and size caps.
|
|
|
3018
2827
|
The frozen legacy dispatcher, source transforms, diagnostic latency loops and
|
|
3019
2828
|
rejected Effect MCP adapters were migration evidence; their historical source
|
|
3020
2829
|
is linked from decision 0016 rather than rebuilt by ordinary tests.
|
|
3021
|
-
The upward MCP protocol leaves remain SDK implementations
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
###
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
on `./activity`, and matching the wire format meant opting out of most of what
|
|
3036
|
-
it does: unowned paths fall through rather than 404, a wrong method is a JSON
|
|
3037
|
-
405, and the content-type and size checks run before a body is read. The root
|
|
3038
|
-
entry imports `effect` and the four public HTTP modules confined to the private
|
|
3039
|
-
dispatcher (`test/purity.node.test.ts`).
|
|
3040
|
-
Effect 4.x is a long-term-support line, but its area barrels `effect/http`,
|
|
3041
|
-
`effect/http-api`, `effect/schema`, and `effect/sql` are still tagged
|
|
3042
|
-
`@stability unstable` and can change within it; only core modules such as
|
|
3043
|
-
`effect/Schema` are stable. That is why Effect is pinned to one exact version
|
|
3044
|
-
and every Effect upgrade is its own deliberate pull request. A deployment
|
|
3045
|
-
that also depends on Effect, such as the Worker example's Alchemy stack, must pin the
|
|
3046
|
-
same version to resolve a single copy. Connecta's own lockfile and packed
|
|
3047
|
-
install are checked for one copy (`test/package-surface.node.test.ts` and
|
|
3048
|
-
`scripts/check-package.mjs`).
|
|
3049
|
-
|
|
3050
|
-
The stable `4.0.0` release was re-evaluated on 2026-10-01 with this
|
|
3051
|
-
[historical probe source](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation.mjs),
|
|
3052
|
-
retained in Git history rather than the current checkout.
|
|
3053
|
-
The [raw observations](https://github.com/zackbart/connecta/blob/1ab92695f2636f904274d900ce209553358ff1f3/scripts/probes/effect-v4-evaluation-2026-10-01.json)
|
|
3054
|
-
are synthetic Node requests and neutral esbuild bundles, not live-client or
|
|
3055
|
-
workerd lifetime verification. Bundle figures add representative prototypes
|
|
3056
|
-
alongside the current code; they do not claim savings from removing old code.
|
|
3057
|
-
|
|
3058
|
-
Two native input schemas with validation and JSON Schema rendering add 62,148
|
|
3059
|
-
bytes gzip to the root, taking it from 303,083 to 365,231 against a 339,526
|
|
3060
|
-
cap. Effect can reject excess properties when both decoding and rendering use
|
|
3061
|
-
`onExcessProperty: "error"`; its default strips them. The generated schemas
|
|
3062
|
-
still differ from the published meta-tool schemas, including record rendering and
|
|
3063
|
-
composed numeric bounds. A Schema replacement would need to preserve that
|
|
3064
|
-
contract and memoized rendering. Both MCP SDK packages still depend on Zod,
|
|
3065
|
-
so changing our inputs alone does not remove Zod from an installation.
|
|
3066
|
-
|
|
3067
|
-
A one-endpoint `HttpApi` prototype adds 106,634 bytes gzip to `./ui` and 106,453
|
|
3068
|
-
to `./activity`. Even plain `HttpRouter` adds 45,324 to `./ui`, taking it to
|
|
3069
|
-
130,023 against a 128,983 cap. Both default handlers return an empty 404 for a
|
|
3070
|
-
wrong method and an unowned path; our routes need a JSON 405 for the former
|
|
3071
|
-
and module fallthrough for the latter. Their `toWebHandler` also builds its
|
|
3072
|
-
layer immediately and owns a runner, so adopting it would require proving
|
|
3073
|
-
global-scope safety and preserving our scheduler and response-lifetime rules.
|
|
3074
|
-
Those historical defaults are not the production adapter: the narrow public
|
|
3075
|
-
HttpRouter dispatch above preserves explicit method refusals, unowned fallthrough
|
|
3076
|
-
and the existing edge runner without `toWebHandler`.
|
|
3077
|
-
|
|
3078
|
-
Effect's MCP server has a separate compatibility gate. Its `2026-07-28`
|
|
3079
|
-
adapter accepted a stateless `tools/list`, but its `2025-06-18` adapter refused
|
|
3080
|
-
a fresh request with 400. Initialization issued a session, and that session's
|
|
3081
|
-
next request returned 404 on a fresh handler; Connecta served the same fresh
|
|
3082
|
-
legacy list with 200 JSON and no session. The stable package exports no MCP
|
|
3083
|
-
client, so it also cannot replace our downstream SDK and OAuth implementation.
|
|
3084
|
-
[Issue #622](https://github.com/zackbart/connecta/issues/622) records the server
|
|
3085
|
-
parity requirements. These historical measurements supported retaining MCP protocol and validation
|
|
3086
|
-
implementations. Production HTTP dispatch now uses the separately proven narrow
|
|
3087
|
-
public adapter above; stable v4 alone did not establish its compatibility.
|
|
3088
|
-
|
|
3089
|
-
The core's cost is recorded rather than guessed. Against 0.24.4 the root entry
|
|
3090
|
-
grew from 235,346 to 279,526 bytes gzip, the Worker example from 263,949 to
|
|
3091
|
-
314,336, and each provider by about 36 KB, since each reaches Effect through
|
|
3092
|
-
`remoteMcp()`. `pnpm run check:bundle` caps the deployed shapes (the root,
|
|
3093
|
-
`./worker`, `./ui`, the operator UI assets, `./d1`, `./auth/access-tokens`,
|
|
3094
|
-
the Worker example, and the other non-provider entries) against
|
|
3095
|
-
`scripts/bundle-budget.json`. Per-provider entries are reported with their
|
|
3096
|
-
size and change from baseline but not capped: every deployment already imports
|
|
3097
|
-
the root, which carries the Effect core, so a provider's standalone bundle
|
|
3098
|
-
counts that core a second time. An install carries about 53 MB of `effect`, with
|
|
3099
|
-
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.
|
|
3100
2844
|
|
|
3101
2845
|
## Where else to look
|
|
3102
2846
|
|
|
@@ -3150,9 +2894,7 @@ compiling and configuring the real thing.
|
|
|
3150
2894
|
accounting and claim ownership keep their existing ordering. Late
|
|
3151
2895
|
rejections are observed; dispatch and lease guards still contain detached
|
|
3152
2896
|
work. Per-await I/O cancellation remains useful for releasing resources,
|
|
3153
|
-
while prompt refusal is enforced at the boundary
|
|
3154
|
-
(`test/cancellation-faults.test.ts`, `test/operation-cancellation.test.ts`
|
|
3155
|
-
and `test/cancellation-journey.node.test.ts`). Invocation owns
|
|
2897
|
+
while prompt refusal is enforced at the boundary. Invocation owns
|
|
3156
2898
|
the recovery and logical-call records it reads after teardown. Closing an
|
|
3157
2899
|
outer call also revokes nested calls paused in preparation. Request-owned
|
|
3158
2900
|
MCP sessions and token flights outlive individual calls and close with the
|
|
@@ -3169,11 +2911,9 @@ compiling and configuring the real thing.
|
|
|
3169
2911
|
transfers preparation, token exchange and fenced rotation commit to the
|
|
3170
2912
|
storage coordinator under its own deadlines and deferred-work hook; only
|
|
3171
2913
|
the caller's wait still closes with the call. That completion
|
|
3172
|
-
cannot resume a closed call or resend its resource request
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
aborts pending authorization-code exchange and refuses late token writes
|
|
3176
|
-
(`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
|
|
3177
2917
|
revokes views and calls first, then runs every finalizer in reverse order,
|
|
3178
2918
|
collecting failures. The caller's original cancellation signal is retained
|
|
3179
2919
|
separately from the lease signal, so normal teardown never looks like caller
|
|
@@ -3247,13 +2987,13 @@ compiling and configuring the real thing.
|
|
|
3247
2987
|
as a `manual` check in its `drift.json`): an API v1 answer carrying Stripe's
|
|
3248
2988
|
`Request-Id`. The real HTTP hostile-response journey checks this refusal
|
|
3249
2989
|
against an ambiguous committed write; there is no copied declaration list.
|
|
3250
|
-
|
|
2990
|
+
Write outcomes are decided in one place, `settleWrite` in
|
|
3251
2991
|
`src/write-outcome.ts`, once per logical call and over every request it
|
|
3252
2992
|
recorded. The invocation calls it for every failure of a served write: a
|
|
3253
2993
|
thrown or mapped error, a custom connector's, a program's call, and its
|
|
3254
2994
|
own deadline or the caller's cancellation, when no connector failure
|
|
3255
2995
|
arrives at all. It reads the logical call's requests
|
|
3256
|
-
(`
|
|
2996
|
+
(`LogicalLedger`'s `sent`), which hold every resource request dispatched
|
|
3257
2997
|
under the call from any ledger: nested `api()` calls, remote MCP, guarded
|
|
3258
2998
|
transports, `ctx.fetch` and the kit. Without an invocation (a direct call,
|
|
3259
2999
|
or a context no invocation opened), only the outermost boundary calls it,
|
|
@@ -3322,9 +3062,8 @@ compiling and configuring the real thing.
|
|
|
3322
3062
|
Remote MCP captures
|
|
3323
3063
|
each physical read's entry before awaiting its response; a later send
|
|
3324
3064
|
cannot overwrite it. The kit's response identity and Google's init identity
|
|
3325
|
-
identify the request whose credential was renewed.
|
|
3326
|
-
|
|
3327
|
-
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)`.
|
|
3328
3067
|
The decider normalizes every failure it settles,
|
|
3329
3068
|
typed or not (a raw network error, an `AbortError`, a thrown string). Its
|
|
3330
3069
|
verdict is final (`settledWrite`, which follows copies and redaction): the
|
|
@@ -3498,23 +3237,8 @@ compiling and configuring the real thing.
|
|
|
3498
3237
|
unchanged for the invocation to report.
|
|
3499
3238
|
Shared provider outcomes and the real HTTP hostile-response journey check
|
|
3500
3239
|
returned data, failure/recovery, privacy and physical writes without a
|
|
3501
|
-
catalog-wide status matrix.
|
|
3502
|
-
|
|
3503
|
-
by refusal, GitHub preflight failures, typed handler errors after successful
|
|
3504
|
-
sends, Forms cursor conflicts after 200, generic-code vendor refusals,
|
|
3505
|
-
auxiliary failures after resource dispatch and concurrent direct-call isolation.
|
|
3506
|
-
It also holds siblings awaiting credentials or already in flight when another
|
|
3507
|
-
write fails, refuses requests after call closure, and probes network errors,
|
|
3508
|
-
refusal-shaped abort reasons and untracked sends. Native transport and
|
|
3509
|
-
request-lifetime suites retain their platform-specific evidence.
|
|
3510
|
-
`test/write-evidence.test.ts` uses real refused, reset, and held sockets on
|
|
3511
|
-
both runtimes: a refused write is unknown and a refused read retryable,
|
|
3512
|
-
including a write aborted with another fetch's real refusal as its reason
|
|
3513
|
-
after the server read its body; `test/write-evidence.node.test.ts` covers a
|
|
3514
|
-
Planning Center POST that undici resends after a 421 to a closed listener, a
|
|
3515
|
-
DELETE that a `RetryAgent` resends after a reset, and a streamed body and a
|
|
3516
|
-
`Blob` with an overridden `stream()` that undici partly sends before its
|
|
3517
|
-
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.
|
|
3518
3242
|
Additional tests:
|
|
3519
3243
|
`test/remote-mcp-write-once.test.ts` (real SDK transport),
|
|
3520
3244
|
`test/api-oauth.test.ts`, `test/google-workspace-delegation.test.ts`, and each
|
|
@@ -3601,10 +3325,10 @@ OAuth retry, grant, response-decoding or ownership orchestration.
|
|
|
3601
3325
|
there before claiming a code/refresh grant, so an unsupported Node host consumes
|
|
3602
3326
|
neither. The base-fetch leaves in static OAuth and remote MCP call
|
|
3603
3327
|
`sendCredentialPost`; remote registration and disconnect revocation do so too.
|
|
3604
|
-
|
|
3328
|
+
The adapters preserve this preflight and the distinction between request-owned code
|
|
3605
3329
|
exchange and coordinator-owned refresh after claim submission. The existing
|
|
3606
3330
|
spent-token algorithm intentionally remains conservative after a submitted claim,
|
|
3607
|
-
even if a later CONNECT fails before destination dispatch.
|
|
3331
|
+
even if a later CONNECT fails before destination dispatch. Remote MCP preserves the
|
|
3608
3332
|
SDK JSON-RPC envelope distinction at the leaf: protocol POSTs are not credential
|
|
3609
3333
|
POSTs; forms and registration JSON are. Discovery GETs remain native fetch.
|
|
3610
3334
|
|
|
@@ -3692,22 +3416,6 @@ unchanged. A narrow machine check restores only those two exact adjacent
|
|
|
3692
3416
|
pairs and requires byte equality, with no other type, export or reference
|
|
3693
3417
|
change. All 88 declarations still exclude Effect and have complete references.
|
|
3694
3418
|
|
|
3695
|
-
The superseding 2026-10-25 D3/D4 cost ruling authorizes only Worker
|
|
3696
|
-
`maxGzip: 785000` (previously 760000) and Clerk `maxGzip: 85000` (previously
|
|
3697
|
-
63087). Official cached Node 22.23.4 / zlib 1.3.1-e00f703 measured Clerk at
|
|
3698
|
-
47,688 -> 80,961 bytes gzip (+33,273): the generated SDK is unchanged, while
|
|
3699
|
-
native Effect adds 91.8 KB raw plus the native helper and small auth-source
|
|
3700
|
-
growth. The isolated D4 candidate measured Worker at 762,666, root at 377,177
|
|
3701
|
-
and D1 at 88,321 bytes gzip. Optional-graph optimization brought UI to 342,955,
|
|
3702
|
-
so the earlier unused UI allowance is revoked and its original 345000 cap
|
|
3703
|
-
remains. Historical baselines, root/D1 and all other caps, dependency pins and
|
|
3704
|
-
security/API gates remain unchanged. These independent measurements do not
|
|
3705
|
-
predict a combined gzip by addition; final artifacts must be rebuilt. Native
|
|
3706
|
-
registration, decorated fallback and request/native-storage semantics also
|
|
3707
|
-
require a smoke through an actual minified production bundle. Final post-hook
|
|
3708
|
-
official artifacts measure root 377,290, D1 88,321, Clerk 80,959, UI 342,959
|
|
3709
|
-
and Worker 762,774 bytes gzip, all within those caps.
|
|
3710
|
-
|
|
3711
3419
|
### Native remote MCP programs (contributor)
|
|
3712
3420
|
|
|
3713
3421
|
Remote MCP connection joins, credential and epoch checks, catalog intake,
|
|
@@ -3731,7 +3439,7 @@ restore that same owner. Every operation captures its own generation, credential
|
|
|
3731
3439
|
identity and listing binding before waiting; a later listing cannot adopt an
|
|
3732
3440
|
abandoned publication. Request closure revokes the connector and cache scopes.
|
|
3733
3441
|
No client, transport, response, signal, promise or live request Context is shared
|
|
3734
|
-
across inbound requests.
|
|
3442
|
+
across inbound requests. The bounded refresh coordinator retains its separate
|
|
3735
3443
|
ownership, synchronous submission gate and waiting-request handoff.
|
|
3736
3444
|
|
|
3737
3445
|
Two deliberate Promise waits remain: a connect waiter receives the Deferred's
|
|
@@ -3770,16 +3478,11 @@ select the underlying current methods at dispatch, including namespaces created
|
|
|
3770
3478
|
while a source method is decorated. Original SQL shells call their own native
|
|
3771
3479
|
program directly. Tests cover current and previously selected programs, own and
|
|
3772
3480
|
inherited saved-original delegates, get/set/CAS and activity record/list,
|
|
3773
|
-
restoration, typed failures and interruption. The
|
|
3481
|
+
restoration, typed failures and interruption. The native auth proof fixtures install
|
|
3774
3482
|
their intentional throwing Promise shells before registration, so they establish
|
|
3775
3483
|
native composition without authorizing bypass of later replacements.
|
|
3776
3484
|
|
|
3777
|
-
|
|
3778
|
-
and workerd. `remote-mcp-native-bundle.node.test.ts` additionally loads an actual
|
|
3779
|
-
minified production bundle to verify call/cache registration, request Context,
|
|
3780
|
-
native storage, saved-original delegates, custom replacements and closed-owner
|
|
3781
|
-
refusal. Existing wire, physical-attempt, cache and OAuth lifetime suites remain
|
|
3782
|
-
the compatibility evidence.
|
|
3485
|
+
Existing wire, cache and OAuth suites remain the compatibility evidence.
|
|
3783
3486
|
|
|
3784
3487
|
### Native upward MCP orchestration (contributor)
|
|
3785
3488
|
|
|
@@ -3833,19 +3536,9 @@ identity, credential, storage, executor and tool callbacks. Connecta's OAuth-lin
|
|
|
3833
3536
|
binding, TTL, progress, closing and one-use claims are native orchestration around
|
|
3834
3537
|
those leaves. The supported protocol and unsupported-feature refusals remain
|
|
3835
3538
|
unchanged. Native consumer, failure, decorator, sharing, close and minified-bundle
|
|
3836
|
-
regressions live in `upward-native.test.ts
|
|
3837
|
-
`upward-native-bundle.node.test.ts`; existing wire, continuation, redaction,
|
|
3539
|
+
regressions live in `upward-native.test.ts`; existing wire, continuation, redaction,
|
|
3838
3540
|
physical-attempt and body/admission suites remain compatibility evidence.
|
|
3839
3541
|
|
|
3840
|
-
The combined native/operator graph has a measured contributor bundle allowance
|
|
3841
|
-
in PR #1060: the UI gzip cap is 346,000 bytes, with its historical baseline
|
|
3842
|
-
unchanged. The official full checker measured 345,228 bytes after regenerating
|
|
3843
|
-
the merged UI assets. Removing unused Invocation/Catalog registration retention
|
|
3844
|
-
returned the public Worker entry from 198,744 to 77,426 bytes; live minified
|
|
3845
|
-
Context, inherited-method, decorator and restoration proofs retain native
|
|
3846
|
-
composition. Root, example Worker, public Worker, D1 and Clerk caps remain
|
|
3847
|
-
unchanged. This cost decision grants no later request-body-limit allowance.
|
|
3848
|
-
|
|
3849
3542
|
### Catalog operation vocabulary (contributor)
|
|
3850
3543
|
|
|
3851
3544
|
The standard hatch privately registers bounded vendor operation text against its
|
|
@@ -3860,13 +3553,3 @@ cache. Exact authored names and complete authored matches precede API pointers;
|
|
|
3860
3553
|
ordinary result limits, offsets, catalog failure disclosure and byte bounds apply.
|
|
3861
3554
|
The published discovery contract states compilation, query and cache bounds.
|
|
3862
3555
|
No request identity, signal, credential or promise enters a vocabulary cache.
|
|
3863
|
-
|
|
3864
|
-
### Authenticated body-bound cost (contributor)
|
|
3865
|
-
|
|
3866
|
-
The separate #860 change has a measured allowance only for the example Worker
|
|
3867
|
-
gzip cap, from 785,000 to 786,000 bytes, with its historical baseline unchanged.
|
|
3868
|
-
The official full checker measures 785,036 bytes against D7's exact 784,948-byte
|
|
3869
|
-
lower graph, an actual 88-byte increase. It reuses the bounded platform body
|
|
3870
|
-
inspector, preserves native orchestration and avoids a new reader lifecycle.
|
|
3871
|
-
Root, UI, public Worker, D1, Clerk and asset caps remain fixed. The cost ruling
|
|
3872
|
-
does not approve protocol, cancellation or authority semantics.
|