@zackbart/connecta 0.36.1 → 0.37.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 +36 -0
- package/bin/version.mjs +1 -1
- package/dist/auth/downstream-oauth.js +34 -12
- package/dist/auth/oauth-authorization-params.js +52 -0
- package/dist/auth/static-oauth.js +4 -6
- package/dist/auth/stored-observation.js +8 -0
- package/dist/call-admission.js +27 -14
- package/dist/connectors/api-connector.js +4 -0
- package/dist/connectors/option-shapes.js +6 -1
- package/dist/connectors/remote-mcp.d.ts +32 -3
- package/dist/connectors/remote-mcp.js +152 -21
- package/dist/execute.js +5 -5
- package/dist/executor-admission.js +27 -13
- package/dist/executors/quickjs.js +4 -1
- package/dist/google.d.ts +13 -0
- package/dist/google.js +42 -0
- package/dist/index.d.ts +2 -1
- package/dist/invocation.js +6 -7
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/model.d.ts +2 -0
- package/dist/providers/_shared/google/service-account.d.ts +1 -1
- package/dist/providers/_shared/google/service-account.js +20 -8
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/request-principal.js +34 -0
- package/dist/request-token.d.ts +53 -0
- package/dist/request-token.js +1 -0
- package/dist/routes/mcp.js +6 -6
- package/dist/routes/ui-config.js +38 -9
- package/dist/routes/ui-data.js +30 -5
- package/dist/routes/ui.js +10 -2
- package/dist/runtime/settlement.js +59 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +4 -1
- package/documentation/README.md +13 -12
- package/documentation/architecture.md +91 -26
- package/documentation/auth.md +317 -12
- package/documentation/code-mode.md +25 -5
- package/documentation/integrating.md +18 -4
- package/documentation/operator-tests.md +19 -0
- package/documentation/operator-ui.md +57 -46
- package/examples/worker/README.md +5 -1
- package/guides/connecting-agents.md +6 -0
- package/guides/safety-and-trust.md +1 -0
- package/package.json +5 -1
- package/templates/node/package.json +1 -1
|
@@ -1199,6 +1199,15 @@ meaning. These facts do not change agent error fields or write outcomes.
|
|
|
1199
1199
|
|
|
1200
1200
|
## Configuration
|
|
1201
1201
|
|
|
1202
|
+
Downstream OAuth browser extensions use `auth.authorizationParams` on
|
|
1203
|
+
`remoteMcp()` and `oauth.authorizationParams` on `api()`. Both snapshot and
|
|
1204
|
+
validate the string map at construction, reserve protocol/security parameters
|
|
1205
|
+
case-insensitively, and add entries only when publishing the consent URL.
|
|
1206
|
+
Remote MCP includes canonical sorted entries in its existing client configuration
|
|
1207
|
+
binding (omitted and empty maps remain equivalent); static API OAuth retains
|
|
1208
|
+
its existing token-endpoint binding. The [auth contract](./auth.md#browser-authorization-parameters)
|
|
1209
|
+
owns limits, reserved names, and reconnect requirements.
|
|
1210
|
+
|
|
1202
1211
|
`ConnectaConfig` is one schema (`src/config.ts`, combinators in
|
|
1203
1212
|
`src/config-schema.ts`). The same value produces the TypeScript type a
|
|
1204
1213
|
deployment writes, the unknown-key walk createConnecta runs before reading any
|
|
@@ -1210,8 +1219,8 @@ is resolved in place of the caller's object, so no getter or Proxy trap runs;
|
|
|
1210
1219
|
an accessor on a config object or array is refused by path, as is an object
|
|
1211
1220
|
that cannot be inspected. A closed object or record slot takes a plain object
|
|
1212
1221
|
only: an array or a class instance there is refused by path, unread. String
|
|
1213
|
-
maps (static `headers`, `api()`
|
|
1214
|
-
`tokenRequestHeaders`) are copied the same way and each value must be a
|
|
1222
|
+
maps (static `headers`, remote MCP and `api()` OAuth `authorizationParams`,
|
|
1223
|
+
and `api()` OAuth `tokenRequestHeaders`) are copied the same way and each value must be a
|
|
1215
1224
|
string. A discriminated union (`remoteMcp()`'s `auth.type`,
|
|
1216
1225
|
a provider's `auth.type`) with a missing or unrecognized discriminant throws
|
|
1217
1226
|
with the valid values. Only plain data is copied. Connectors, modules,
|
|
@@ -1744,6 +1753,20 @@ Use the existing stateful fixture vendors and published-edge deployment adapters
|
|
|
1744
1753
|
Native transport parity, OAuth and write-provenance suites retain their
|
|
1745
1754
|
workerd coverage. The shared throwaway RSA key in `test/fixtures/rsa.ts` is
|
|
1746
1755
|
used by write-provenance and unknown-write-advice fixtures.
|
|
1756
|
+
- `test/executor-requests.test.ts` runs overlapping `execute_code` programs
|
|
1757
|
+
as separate workerd requests through the real Worker Loader executor, with
|
|
1758
|
+
real TCP host calls that read their own request's signal. Queued programs
|
|
1759
|
+
and the programs whose release admitted them return their own answers; queue
|
|
1760
|
+
refusals keep the retryable shape. Its fixture applies workerd's request
|
|
1761
|
+
binding of async-context frames, which the pinned runtime predates, through a
|
|
1762
|
+
signal each request owns, so it refuses another request's entry with or
|
|
1763
|
+
without an async-context token, as workerd does; pointing
|
|
1764
|
+
`MINIFLARE_WORKERD_PATH` at a newer workerd runs the native check.
|
|
1765
|
+
`test/executor-native.test.ts` shows that a slot granted just before
|
|
1766
|
+
`close()` never starts a program on the native Worker Loader.
|
|
1767
|
+
`test/request-context.node.test.ts` pins the same wake order and async context
|
|
1768
|
+
on Node for executor and call admission and QuickJS host calls, and gives back
|
|
1769
|
+
grants that shutdown overtook.
|
|
1747
1770
|
- The request, catalog, auxiliary-call and renewal lifetime
|
|
1748
1771
|
suites, plus `test/auth-admission-binding.test.ts`, exercise cancellation,
|
|
1749
1772
|
held native work and garbage collection. These test actual request ownership;
|
|
@@ -1770,6 +1793,28 @@ fetch, streams, Web Crypto, lifetime and request-byte behavior, as listed in
|
|
|
1770
1793
|
|
|
1771
1794
|
## Optional deployment modules
|
|
1772
1795
|
|
|
1796
|
+
`/google` (`src/google.ts`) exports the Promise-based
|
|
1797
|
+
`googleServiceAccountToken` request-token resolver outside the root graph.
|
|
1798
|
+
It reuses `src/providers/_shared/google/service-account.ts` for Web Crypto,
|
|
1799
|
+
in-memory token caching and exact-token invalidation, with an absent subject
|
|
1800
|
+
for service accounts acting as themselves. Workspace providers keep supplying
|
|
1801
|
+
subjects and retain their delegated error guidance. `src/request-token.ts`
|
|
1802
|
+
owns the callable resolver contract and optional HTTP-401 invalidation hook;
|
|
1803
|
+
`remoteMcp()` invokes it without replaying the failed operation. With
|
|
1804
|
+
`auth.principal`, `src/request-principal.ts` reads the admitted caller only
|
|
1805
|
+
through `callerOf` (see `src/connector-caller.ts`), refuses callers the policy
|
|
1806
|
+
does not accept before resolving, and gives `remoteMcp()` a principal key that
|
|
1807
|
+
joins the credential digest, so no client crosses principals. A custom `fetch`
|
|
1808
|
+
replaces global fetch and the owned write sender in that connector's transport,
|
|
1809
|
+
behind the same redirect wrapper and a same-origin guard.
|
|
1810
|
+
`test/remote-mcp-principal-forwarding.test.ts` checks admission, forging,
|
|
1811
|
+
client replacement, transport confinement and secrecy on Node, and a real
|
|
1812
|
+
service binding on workerd.
|
|
1813
|
+
`test/google-service-account-token.test.ts` exercises signing/cache behavior on
|
|
1814
|
+
Node and workerd; `test/google-service-account-remote.node.test.ts` checks
|
|
1815
|
+
real-socket token/MCP requests, invalidation and one physical attempt per write.
|
|
1816
|
+
Operator setup lives in [Google hosted MCP](./auth.md#google-service-account-tokens-and-hosted-mcp).
|
|
1817
|
+
|
|
1773
1818
|
`createConnecta` takes closed typed `ui`, `vault`, and `activity`
|
|
1774
1819
|
slots, with factories at `/ui`, `/credentials`, and `/activity`
|
|
1775
1820
|
and agent tokens at `/auth/access-tokens`. Root exports the contracts, never the implementations, and there
|
|
@@ -2004,11 +2049,13 @@ existing execute pipeline, using its request identity and call capabilities.
|
|
|
2004
2049
|
The executor seam creates no second request or call owner.
|
|
2005
2050
|
|
|
2006
2051
|
Both built-ins acquire their permits through native admission and return
|
|
2007
|
-
execute-once leases with synchronous, idempotent release.
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
`
|
|
2011
|
-
|
|
2052
|
+
execute-once leases with synchronous, idempotent release. Admission wakes a
|
|
2053
|
+
queued waiter through a Settlement, so it resumes in its own request
|
|
2054
|
+
([across requests](#across-requests)). A queued standalone execution also
|
|
2055
|
+
crosses `awaitExecutorLease`'s Promise handoff before host setup, and
|
|
2056
|
+
`execute_code` retains its public `acquire()` handoff. A cancelled queued
|
|
2057
|
+
handoff withdraws its waiter, and a late custom acquisition releases its
|
|
2058
|
+
abandoned lease.
|
|
2012
2059
|
|
|
2013
2060
|
The Worker SDK prepares source and dispatchers against an inert loader. The
|
|
2014
2061
|
native host then acquires the actual Loader handle and RPC entrypoint with
|
|
@@ -2024,7 +2071,9 @@ Both built-ins normalize provider leaf returns with `Promise.resolve`, retaining
|
|
|
2024
2071
|
direct JavaScript callers' synchronous value tolerance and original host
|
|
2025
2072
|
rejections. The published provider-function signature remains Promise-shaped.
|
|
2026
2073
|
|
|
2027
|
-
QuickJS owns host-call fibers in each active run's Scope
|
|
2074
|
+
QuickJS owns host-call fibers in each active run's Scope, started in the
|
|
2075
|
+
async context the run captured: a warm child's IPC events arrive in the
|
|
2076
|
+
context of the request that spawned it. Completion,
|
|
2028
2077
|
interruption, cancellation, recycling and shutdown revoke provider lookup and
|
|
2029
2078
|
interrupt those fibers; late SDK results and stale IPC cannot reply to another
|
|
2030
2079
|
run. Releasing a lease during readiness or crash backoff also cancels the old
|
|
@@ -2209,24 +2258,40 @@ request's abort handler also releases them idempotently if workerd discards the
|
|
|
2209
2258
|
pending continuation on disconnect. Membership cache
|
|
2210
2259
|
hits precede these bounds; see [organization admission](./auth.md#one-clerk-instance-one-organization-per-deployment).
|
|
2211
2260
|
|
|
2212
|
-
Asynchronous work shared across requests meets only through a
|
|
2213
|
-
request
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
inside the
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2261
|
+
Asynchronous work shared across requests meets only through a `Settlement`
|
|
2262
|
+
(`src/runtime/settlement.ts`) that one request settles, never through a fiber
|
|
2263
|
+
that outlives its request or a Deferred another request awaits. Two workerd
|
|
2264
|
+
rules follow, and both have bitten.
|
|
2265
|
+
|
|
2266
|
+
**No request resumes another request's fiber.** Deferred resumes a waiting
|
|
2267
|
+
fiber synchronously, inside the call that completed it, and Effect re-enters
|
|
2268
|
+
the async context the fiber suspended in through the `AsyncResource` it
|
|
2269
|
+
captured. On Workers that context belongs to the waiter's request. Since
|
|
2270
|
+
workerd's September 2026 releases, a Worker with compatibility date 2025-06-16
|
|
2271
|
+
or later (or `bind_asynclocalstorage_snapshot_to_request`) refuses that entry
|
|
2272
|
+
from any other request: "Cannot call this AsyncLocalStorage bound function
|
|
2273
|
+
outside of the request in which it was created." The throw escapes the
|
|
2274
|
+
completing call, so the request that released an admission slot failed with
|
|
2275
|
+
`executor_failed`, and the stranded waiter later timed out (#1106). Older
|
|
2276
|
+
runtimes ran the waiter in the completing request instead, where its own I/O
|
|
2277
|
+
failed with "Cannot perform I/O on behalf of a different request". Awaiting a
|
|
2278
|
+
Promise around the wait fixes neither: the fiber waiting inside that Promise is
|
|
2279
|
+
the one resumed. A Settlement records its outcome synchronously, for queue
|
|
2280
|
+
arbitration and late-grant cleanup, and wakes each waiter by resolving a
|
|
2281
|
+
Promise the waiter's own request created. workerd runs that Promise's reactions
|
|
2282
|
+
in the request that created it, and Node runs them a microtask later in the
|
|
2283
|
+
waiter's async context. That wake is a turn late, so whatever a waiter acts on
|
|
2284
|
+
it rechecks there: an admission queue gives back a grant its controller closed
|
|
2285
|
+
in between, while a grant that beat the waiter's own abort stays the waiter's to
|
|
2286
|
+
release, and a refresh waiter rechecks its own signal before using a flight's
|
|
2287
|
+
answer. Executor admission, `/mcp` request
|
|
2288
|
+
admission, connector call admission and OAuth refresh flights all queue through
|
|
2289
|
+
Settlements; Effect's `Semaphore` is not used across requests for the same
|
|
2290
|
+
reason, and because a same-tick newcomer can take a released permit ahead of a
|
|
2291
|
+
waiter that queued earlier. Events from shared native objects follow the same
|
|
2292
|
+
rule: a warm QuickJS child's IPC messages arrive in the async context of the
|
|
2293
|
+
request that spawned it, so host calls dispatch inside the running program's
|
|
2294
|
+
captured context.
|
|
2230
2295
|
|
|
2231
2296
|
**Never read another request's signal.** On workerd, reading `aborted` or
|
|
2232
2297
|
`reason`, or calling `abort()`, on a signal that belongs to another request
|
package/documentation/auth.md
CHANGED
|
@@ -6,16 +6,16 @@
|
|
|
6
6
|
and package maintenance. This guide owns identity, membership, and downstream
|
|
7
7
|
credential contracts; use [deployment setup](./deploying.md) for runnable steps.
|
|
8
8
|
|
|
9
|
-
| Task | Sections
|
|
10
|
-
| ------------------------------------------------------------------- |
|
|
11
|
-
| Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients)
|
|
12
|
-
| Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth)
|
|
13
|
-
| Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools)
|
|
14
|
-
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management)
|
|
15
|
-
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting)
|
|
16
|
-
| Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say)
|
|
17
|
-
| Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens)
|
|
18
|
-
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant)
|
|
9
|
+
| Task | Sections |
|
|
10
|
+
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| Connect a machine or migrate an old client | [Agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens), [agent endpoints](#agent-endpoints), [static bearer migration](#migrating-static-bearer-clients) |
|
|
12
|
+
| Configure caller visibility, read-only endpoints, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
|
|
13
|
+
| Lay out an agent's pools | [Organizing an agent's pools](#organizing-an-agents-pools) |
|
|
14
|
+
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [Clerk Organizations](#one-clerk-instance-one-organization-per-deployment), [management](#human-authentication-management) |
|
|
15
|
+
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
|
|
16
|
+
| Diagnose consent or token refresh | [State at rest](#downstream-oauth-state-at-rest), [callbacks](#consents-and-callbacks), [refresh failures](#refresh-failures), [safe errors](#what-a-servers-errors-may-say) |
|
|
17
|
+
| Configure a downstream OAuth client or HTTP handler | [Client registration](#downstream-oauth-client-registration), [api() OAuth](#downstream-oauth-on-api), [request-local bearer tokens](#request-local-downstream-bearer-tokens), [principal forwarding](#forwarding-the-admitted-principal), [Google hosted MCP](#google-service-account-tokens-and-hosted-mcp) |
|
|
18
|
+
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
|
|
19
19
|
|
|
20
20
|
[Credential recognition and admission](#credential-recognition-and-admission)
|
|
21
21
|
defines refusal precedence.
|
|
@@ -1336,6 +1336,29 @@ healthy. Restart invalidates it even if the connection ends healthy. Continue
|
|
|
1336
1336
|
trusts the stored registration's `redirect_uris`; after changing `publicUrl`,
|
|
1337
1337
|
Restart registers for the current callback URL.
|
|
1338
1338
|
|
|
1339
|
+
For `remoteMcp()` OAuth, the owner's stored grant decides whether a connection
|
|
1340
|
+
is connected, not whether the downstream answers. Some servers answer
|
|
1341
|
+
`initialize` and `tools/list` without credentials and challenge only
|
|
1342
|
+
`tools/call`. The operator status, the UI's connected state, and a start agree
|
|
1343
|
+
with the gate a call passes:
|
|
1344
|
+
|
|
1345
|
+
| Stored grant | Status | Start (Connect, Continue, Restart, `authorize_connector`) |
|
|
1346
|
+
| ------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1347
|
+
| Disconnected | `auth_required`; the downstream is not asked | Replaces the epoch, then proceeds as for no tokens |
|
|
1348
|
+
| No tokens | Connects; `auth_required` even when the downstream answers | Connects first, so a 401's `resource_metadata` starts consent as before. A downstream that answers without credentials still gets consent, discovered from RFC 9728 metadata at the configured URL's well-known locations. The start answers `auth_required` with the consent URL. |
|
|
1349
|
+
| Tokens (any expiry) | Connects; `ok`, or `auth_required` when a 401 needs consent | Connects; `ok` without starting a flow when healthy |
|
|
1350
|
+
|
|
1351
|
+
A start without a challenge runs the same SDK flow as a 401 without
|
|
1352
|
+
`resource_metadata`. Issuer and resource checks, consent state, PKCE, and
|
|
1353
|
+
the CIMD, registration, and static-client order are unchanged, and nothing is
|
|
1354
|
+
sent to the MCP endpoint. A call never starts consent. A call's 401 that names
|
|
1355
|
+
per-tool metadata, such as `/.well-known/oauth-protected-resource/<tool>`,
|
|
1356
|
+
renews through it when the grant holds no discovery. That metadata must name
|
|
1357
|
+
the configured resource, as any other must. When renewal is refused, the grant
|
|
1358
|
+
loses its tokens and the next start hands out consent. Restart (`force: true`)
|
|
1359
|
+
removes the tokens first, so it starts consent unless the downstream or
|
|
1360
|
+
discovery fails.
|
|
1361
|
+
|
|
1339
1362
|
### Consents and callbacks
|
|
1340
1363
|
|
|
1341
1364
|
A start stores its flow record, then the grant's pointer to it. A reset that
|
|
@@ -2498,6 +2521,48 @@ stored static-client state and reselects the configured identity.
|
|
|
2498
2521
|
Configured `scope` is a fallback; a downstream challenge or protected-resource
|
|
2499
2522
|
declaration takes precedence, and the SDK adds `offline_access` when advertised.
|
|
2500
2523
|
|
|
2524
|
+
### Browser authorization parameters
|
|
2525
|
+
|
|
2526
|
+
Set `auth.authorizationParams` on an OAuth `remoteMcp()` connector to add
|
|
2527
|
+
non-secret provider parameters to the browser consent URL. This works with
|
|
2528
|
+
pre-registered clients, CIMD, and DCR. For example:
|
|
2529
|
+
|
|
2530
|
+
```ts
|
|
2531
|
+
remoteMcp("crm", {
|
|
2532
|
+
url: "https://mcp.example/mcp",
|
|
2533
|
+
auth: {
|
|
2534
|
+
type: "oauth",
|
|
2535
|
+
scope: "records.read",
|
|
2536
|
+
authorizationParams: { access_type: "offline", prompt: "consent" },
|
|
2537
|
+
},
|
|
2538
|
+
});
|
|
2539
|
+
```
|
|
2540
|
+
|
|
2541
|
+
Each configured parameter occurs once, replacing any same-name value already
|
|
2542
|
+
on the authorization endpoint URL. These parameters are never added to token
|
|
2543
|
+
exchange, refresh, registration, revocation, metadata, or MCP requests.
|
|
2544
|
+
Descriptions expose parameter names only. Values are public configuration,
|
|
2545
|
+
visible in the consent URL and stored with it; never put secrets here.
|
|
2546
|
+
|
|
2547
|
+
The map must be a plain string record with at most 32 entries. Names contain
|
|
2548
|
+
1–128 ASCII letters, digits, dots, underscores, or hyphens. Values must be
|
|
2549
|
+
nonempty (not whitespace-only), at most 2048 UTF-16 code units, and contain no
|
|
2550
|
+
ASCII control characters. Case-variant duplicate names are refused. The
|
|
2551
|
+
following names, including every case variant, fail construction with
|
|
2552
|
+
`ConfigError`: `response_type`, `response_mode`, `client_id`, `client_secret`,
|
|
2553
|
+
`redirect_uri`, `state`, `code_challenge`, `code_challenge_method`, `scope`,
|
|
2554
|
+
`resource`, `request`, and `request_uri`. Configure scopes through `auth.scope`.
|
|
2555
|
+
`response_mode` is reserved because Connecta accepts callbacks only as GET query
|
|
2556
|
+
parameters.
|
|
2557
|
+
|
|
2558
|
+
For remote MCP, the map joins the stored client configuration binding, like
|
|
2559
|
+
scope and client identity. Adding, removing, or changing a parameter requires
|
|
2560
|
+
Restart or Disconnect and reconnect to obtain a grant with the new consent
|
|
2561
|
+
settings. The map does not rewrite existing grants or pending consent URLs;
|
|
2562
|
+
as with scope, reconfiguring it alone does not obtain a new grant. A DCR
|
|
2563
|
+
registration created under different parameters cannot be carried through
|
|
2564
|
+
Restart. Key order is irrelevant; an empty map and omission are equivalent.
|
|
2565
|
+
|
|
2501
2566
|
## Downstream OAuth on `api()`
|
|
2502
2567
|
|
|
2503
2568
|
`api()` takes a static authorization-code configuration for APIs without MCP
|
|
@@ -2532,7 +2597,7 @@ No discovery or registration occurs; all URLs are configuration, so the
|
|
|
2532
2597
|
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
2533
2598
|
| Endpoints | Checked at construction: HTTPS, HTTP only on loopback, no credentials or fragment. |
|
|
2534
2599
|
| `pkce` | S256 by default; `false` omits challenge and verifier. |
|
|
2535
|
-
| `authorizationParams` |
|
|
2600
|
+
| `authorizationParams` | Browser-only string parameters. Exact names `response_type`, `client_id`, `redirect_uri`, `state`, `scope`, `code_challenge`, `code_challenge_method` and `resource` are refused; other names and string values are accepted as before. Remote MCP's [stricter rules](#browser-authorization-parameters) do not apply here. |
|
|
2536
2601
|
| `expectedCallbackParameters` | Optional `Readonly<Record<string, string>>` of fixed provider response values. Each configured parameter must occur exactly once with its exact decoded value, after pending-state verification and before issuer validation, flow claims, code exchange, or grant persistence. Missing, duplicate, or mismatched values refuse without exchanging a code or returning parameter values. Omission preserves existing behavior. |
|
|
2537
2602
|
| `revocationEndpoint` | Optional trusted HTTPS revocation URL (loopback HTTP allowed), without credentials, query or fragment. Disconnect removes the local grant before one bounded attempt using the configured client. Failure reports `oauth_revocation_failed`; a repeated Disconnect never retries. |
|
|
2538
2603
|
| `revocationRequestFormat` | `"form"` by default; `"json"` sends JSON without `token_type_hint`. Requires `revocationEndpoint` when set. |
|
|
@@ -2585,6 +2650,15 @@ owner's access token as `Authorization: Bearer` with these rules:
|
|
|
2585
2650
|
- The answer's `.text()` and `.json()`, clones' too, decode its bytes as UTF-8, so
|
|
2586
2651
|
workerd never quotes a downstream's Content-Type in its own log.
|
|
2587
2652
|
|
|
2653
|
+
`api()` already supports `oauth.authorizationParams` through the same OAuth
|
|
2654
|
+
provider and browser-only injection. It keeps its existing, more permissive
|
|
2655
|
+
validation (see the `api()` option table), so existing configurations still
|
|
2656
|
+
construct. Use only GET query response modes with it. Its existing binding remains
|
|
2657
|
+
unchanged: the token endpoint identifies the grant; scope, client identity,
|
|
2658
|
+
and authorization parameters do not add a stored client configuration binding.
|
|
2659
|
+
Changing authorization parameters affects new consents; reconnect to obtain a
|
|
2660
|
+
grant with different consent settings.
|
|
2661
|
+
|
|
2588
2662
|
The grant record and consents, epoch fencing, vault sealing, shared/personal ownership,
|
|
2589
2663
|
callback state/principal checks, `authorize_connector`, Connect, Restart, and
|
|
2590
2664
|
Disconnect match `remoteMcp()`. Status calls report a stored grant for the
|
|
@@ -2597,12 +2671,26 @@ For providers offering both, deployment config chooses; handlers check `ctx.oaut
|
|
|
2597
2671
|
|
|
2598
2672
|
`remoteMcp("<id>", { auth: { type: "request", token: async (ctx) => … } })` resolves
|
|
2599
2673
|
its token before connecting and before reusing a client. The callback receives
|
|
2600
|
-
the operation's cancellation context
|
|
2674
|
+
the operation's cancellation context only. It never receives an agent-selected
|
|
2675
|
+
credential owner, and without the opt-in below it never receives the caller.
|
|
2601
2676
|
Only shared ownership is accepted. Tokens remain inside the request's hardened
|
|
2602
2677
|
transport, are not stored or described, and a changed token replaces the client.
|
|
2603
2678
|
This mode refuses cleartext non-loopback origins. Optional `auth.headers` carry
|
|
2604
2679
|
static protocol/catalog controls and cannot set Authorization.
|
|
2605
2680
|
|
|
2681
|
+
Return the same token for the same caller while it remains valid, and cache a
|
|
2682
|
+
minted token until shortly before it expires. A value that changes on every
|
|
2683
|
+
resolution replaces the client each time; a change that lands while a call
|
|
2684
|
+
connects fails that call with a retryable error.
|
|
2685
|
+
|
|
2686
|
+
A resolver may expose `token.invalidate(rejectedToken, ctx): Promise<void>` to
|
|
2687
|
+
remove a cached token after a downstream HTTP 401. The hook receives the exact
|
|
2688
|
+
token sent by that transport, so it must leave a newer cached token intact.
|
|
2689
|
+
Hook failures are withheld and preserve the original 401. Request auth never
|
|
2690
|
+
automatically repeats the operation or renews the grant inside the failed
|
|
2691
|
+
scope: authorization failure stays latched for that scope. A new request resolves
|
|
2692
|
+
the callback again. In particular, no dispatched write is replayed.
|
|
2693
|
+
|
|
2606
2694
|
The GitHub provider validates each tool's owner/repo and scope access before
|
|
2607
2695
|
resolving an installation token. It creates one token-bound MCP client for that
|
|
2608
2696
|
operation and closes it before returning. Completed installation/token caches
|
|
@@ -2613,6 +2701,223 @@ is retained across requests, and rejected writes are never replayed. An omitted
|
|
|
2613
2701
|
`describe()` includes neither the key nor the minted tokens. The maintained
|
|
2614
2702
|
provider skill explains App permissions and installation prerequisites.
|
|
2615
2703
|
|
|
2704
|
+
### Forwarding the admitted principal
|
|
2705
|
+
|
|
2706
|
+
Set `auth.principal` instead of `auth.token` to let the resolver act for the
|
|
2707
|
+
caller inbound auth admitted. Use it only for a downstream the deployment
|
|
2708
|
+
controls and that already trusts this deployment, so the downstream needs no
|
|
2709
|
+
per-person OAuth:
|
|
2710
|
+
|
|
2711
|
+
```ts
|
|
2712
|
+
auth: {
|
|
2713
|
+
type: "request",
|
|
2714
|
+
principal: {
|
|
2715
|
+
accept: "human",
|
|
2716
|
+
token: async (ctx, { principal }) => await notesAssertion(principal),
|
|
2717
|
+
},
|
|
2718
|
+
},
|
|
2719
|
+
```
|
|
2720
|
+
|
|
2721
|
+
- `principal` is `{ kind: "human", namespace, id }`: the admitted person's
|
|
2722
|
+
stable reference, the same `{ namespace, id }` a pool member resolves to.
|
|
2723
|
+
Inbound auth admits no verified email or display name into it; map the id in
|
|
2724
|
+
deployment code or in the downstream when one is needed.
|
|
2725
|
+
- `accept: "human"` admits only people. Agents, anonymous callers,
|
|
2726
|
+
principal-less machine credentials and contexts no request admitted are
|
|
2727
|
+
refused with `auth_required` before the resolver runs. Agents still see the
|
|
2728
|
+
connector, and their calls fail. No identity is ever substituted.
|
|
2729
|
+
- `accept: "human-or-agent"` also admits declared agents as
|
|
2730
|
+
`{ kind: "agent", name, pool }`, the pool being the one the agent arrived on.
|
|
2731
|
+
An agent is never presented as a person, its pool's owner included.
|
|
2732
|
+
- The caller comes only from the authorization that admitted the request.
|
|
2733
|
+
Tool arguments, program code and request headers cannot choose or change it.
|
|
2734
|
+
- The principal joins the client's identity. A different principal never
|
|
2735
|
+
reuses a client, connection or connect in flight, even when the resolver
|
|
2736
|
+
returns the same token. `token.invalidate(rejected, ctx, { principal })`
|
|
2737
|
+
receives the principal the rejected token was minted for.
|
|
2738
|
+
- Catalog listing needs an admitted caller like any call, and its cache stays
|
|
2739
|
+
partitioned per caller. Operator status and catalog checks carry no caller,
|
|
2740
|
+
so they report `auth_required` without connecting.
|
|
2741
|
+
- The connector stays shared. Connecta keeps no per-principal credential state
|
|
2742
|
+
and never stores, logs or describes the principal or the token. `describe()`
|
|
2743
|
+
reports `mode: "request"` as for any request resolver.
|
|
2744
|
+
|
|
2745
|
+
#### Over a Workers service binding
|
|
2746
|
+
|
|
2747
|
+
Pair principal forwarding with the optional `fetch` to reach a sibling Worker
|
|
2748
|
+
through a service binding. Then neither Cloudflare Access, the public internet
|
|
2749
|
+
nor a service token lies between the two Workers:
|
|
2750
|
+
|
|
2751
|
+
```ts
|
|
2752
|
+
const notes = remoteMcp("notes", {
|
|
2753
|
+
url: "https://notes.example.com/mcp",
|
|
2754
|
+
fetch: (input, init) => env.NOTES.fetch(input, init),
|
|
2755
|
+
auth: {
|
|
2756
|
+
type: "request",
|
|
2757
|
+
principal: {
|
|
2758
|
+
accept: "human",
|
|
2759
|
+
token: async (_ctx, { principal }) => await notesAssertion(principal, env.NOTES_ASSERTION_KEY),
|
|
2760
|
+
},
|
|
2761
|
+
},
|
|
2762
|
+
});
|
|
2763
|
+
```
|
|
2764
|
+
|
|
2765
|
+
`notesAssertion` is deployment code. It signs a short-lived assertion naming
|
|
2766
|
+
`principal.namespace` and `principal.id` with a key both Workers hold, and
|
|
2767
|
+
caches it per principal until shortly before it expires. Over a binding, the
|
|
2768
|
+
token may instead be an unsigned principal reference, provided the downstream
|
|
2769
|
+
reads it only where the trust rule below holds.
|
|
2770
|
+
|
|
2771
|
+
The downstream must accept a forwarded identity only through its binding
|
|
2772
|
+
entry point or after verifying the signature. Bind to a named entrypoint, such
|
|
2773
|
+
as `{ "binding": "NOTES", "service": "notes", "entrypoint": "Connecta" }` in
|
|
2774
|
+
the deployment's Wrangler `services`, and read the identity only in that
|
|
2775
|
+
entrypoint. Never accept it on a public route: anyone who can reach that route
|
|
2776
|
+
could name any person. A signature verified with a deployment-held key is also
|
|
2777
|
+
safe on a public route.
|
|
2778
|
+
|
|
2779
|
+
`fetch` carries every MCP request, including handshakes, catalog reads, calls,
|
|
2780
|
+
writes and session close. Global fetch is never used for that connector. The
|
|
2781
|
+
configured `url` stays the logical origin: its HTTPS rule, cleartext refusal,
|
|
2782
|
+
redirect policy and same-origin redirect checks apply unchanged. A request for
|
|
2783
|
+
any other origin is refused before it reaches `fetch`. Each request, writes
|
|
2784
|
+
included, is handed to `fetch` once with `redirect: "manual"`, and a dispatched
|
|
2785
|
+
write is never sent again, so `fetch` must not retry or follow redirects. On
|
|
2786
|
+
Node, such writes do not need the owned transport. `fetch` cannot be combined
|
|
2787
|
+
with OAuth auth, because discovery, registration and token requests go to
|
|
2788
|
+
authorization servers the downstream names.
|
|
2789
|
+
|
|
2790
|
+
Contributor evidence: `test/remote-mcp-principal-forwarding.test.ts` runs on
|
|
2791
|
+
Node and, through a real service binding, on workerd.
|
|
2792
|
+
|
|
2793
|
+
### Google service-account tokens and hosted MCP
|
|
2794
|
+
|
|
2795
|
+
Import `googleServiceAccountToken` from `@zackbart/connecta/google` to connect a
|
|
2796
|
+
Google hosted MCP server using a service-account JSON key on Node or Workers.
|
|
2797
|
+
This optional subpath is outside the root import graph. Supply deployment secrets
|
|
2798
|
+
through the environment or platform secret store:
|
|
2799
|
+
|
|
2800
|
+
```ts
|
|
2801
|
+
import { remoteMcp } from "@zackbart/connecta";
|
|
2802
|
+
import { googleServiceAccountToken } from "@zackbart/connecta/google";
|
|
2803
|
+
|
|
2804
|
+
const warehouse = remoteMcp("warehouse", {
|
|
2805
|
+
url: "https://bigquery.googleapis.com/mcp",
|
|
2806
|
+
authScope: "shared",
|
|
2807
|
+
auth: {
|
|
2808
|
+
type: "request",
|
|
2809
|
+
token: googleServiceAccountToken({
|
|
2810
|
+
serviceAccount: env.GOOGLE_SERVICE_ACCOUNT_JSON,
|
|
2811
|
+
scopes: ["https://www.googleapis.com/auth/bigquery"],
|
|
2812
|
+
}),
|
|
2813
|
+
},
|
|
2814
|
+
});
|
|
2815
|
+
|
|
2816
|
+
const warehousePersonal = remoteMcp("warehouse_personal", {
|
|
2817
|
+
url: "https://bigquery.googleapis.com/mcp",
|
|
2818
|
+
authScope: "personal",
|
|
2819
|
+
auth: {
|
|
2820
|
+
type: "oauth",
|
|
2821
|
+
scope: "https://www.googleapis.com/auth/bigquery",
|
|
2822
|
+
authorizationParams: { access_type: "offline", prompt: "consent" },
|
|
2823
|
+
client: {
|
|
2824
|
+
issuer: "https://accounts.google.com",
|
|
2825
|
+
clientId: env.GOOGLE_CLIENT_ID,
|
|
2826
|
+
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
|
2827
|
+
tokenEndpointAuthMethod: "client_secret_post",
|
|
2828
|
+
},
|
|
2829
|
+
},
|
|
2830
|
+
});
|
|
2831
|
+
```
|
|
2832
|
+
|
|
2833
|
+
`serviceAccount` accepts downloaded JSON text or
|
|
2834
|
+
`{ clientEmail, privateKey, clientId? }`; `privateKey` is a PKCS#8 RSA PEM of at
|
|
2835
|
+
least 2048 bits. `scopes` must be a non-empty array of HTTPS URLs without
|
|
2836
|
+
credentials, whitespace, or fragments. Unknown options fail construction with
|
|
2837
|
+
`ConfigError`. An optional fixed `subject: "alice@example.com"` requests
|
|
2838
|
+
Workspace domain-wide delegation and requires its admin grant. Omit `subject`
|
|
2839
|
+
for BigQuery: the signed assertion then has no `sub` and acts as the service
|
|
2840
|
+
account itself. See [Google's service-account grant documentation](https://developers.google.com/identity/protocols/oauth2/service-account).
|
|
2841
|
+
|
|
2842
|
+
The helper signs RS256 JWT-bearer assertions and exchanges them at Google's fixed
|
|
2843
|
+
token endpoint. It keeps only bounded in-memory token entries, partitioned by
|
|
2844
|
+
account, key fingerprint, absent-or-present subject and scope set. Concurrent
|
|
2845
|
+
mints share a flight; tokens refresh with a one-minute margin. Keys, assertions
|
|
2846
|
+
and tokens are never logged, persisted or described. Bearer tokens use request
|
|
2847
|
+
auth's existing secret redaction. A 401 evicts only the rejected token; the next
|
|
2848
|
+
request mints again. The failed operation is never automatically repeated.
|
|
2849
|
+
|
|
2850
|
+
#### Separate agent and people connections
|
|
2851
|
+
|
|
2852
|
+
Grant the shared connection to agent pools (including pools used by Eve agents)
|
|
2853
|
+
and the personal connection to pools used by people. Add these entries to the
|
|
2854
|
+
existing deployment configuration, keeping its inbound auth, executor, vault and
|
|
2855
|
+
storage configuration:
|
|
2856
|
+
|
|
2857
|
+
```ts
|
|
2858
|
+
connectors: [warehouse, warehousePersonal],
|
|
2859
|
+
agents: ["assistant"],
|
|
2860
|
+
pools: {
|
|
2861
|
+
warehouse_agents: { tools: ["warehouse"], members: ["agent:assistant"] },
|
|
2862
|
+
warehouse_people: { tools: ["warehouse_personal"], members: ["user_alice"] },
|
|
2863
|
+
},
|
|
2864
|
+
```
|
|
2865
|
+
|
|
2866
|
+
Replace `user_alice` with Alice's admitted principal ID. Agents have no principal;
|
|
2867
|
+
`authScope: "personal"` connectors never appear for them, even if included in a
|
|
2868
|
+
pool's tools. The shared connection uses its fixed service account for every
|
|
2869
|
+
member; the personal connection uses each person's own encrypted OAuth grant.
|
|
2870
|
+
People connect through `authorize_connector` or their member page. Follow
|
|
2871
|
+
[Add a person](#add-a-person), [agent tokens](#agent-tokens-and-upgrading-from-earlier-tokens)
|
|
2872
|
+
and [shared credential ownership](#connect-a-shared-credential) for admission and
|
|
2873
|
+
pool membership. The request-token connection gets its key from deployment
|
|
2874
|
+
configuration and does not need a credential-vault slot.
|
|
2875
|
+
|
|
2876
|
+
#### Google-side setup
|
|
2877
|
+
|
|
2878
|
+
1. Enable the BigQuery API in the Google Cloud project and confirm that the
|
|
2879
|
+
BigQuery MCP service is available under the project's organization policies.
|
|
2880
|
+
Google's current setup uses `https://bigquery.googleapis.com/mcp` and API
|
|
2881
|
+
enablement; follow its current server setup if enablement requirements change.
|
|
2882
|
+
Grant the service account MCP Tool User (`roles/mcp.toolUser`) and BigQuery
|
|
2883
|
+
Job User (`roles/bigquery.jobUser`) on the project where queries run.
|
|
2884
|
+
[BigQuery MCP setup and required roles](https://docs.cloud.google.com/bigquery/docs/use-bigquery-mcp).
|
|
2885
|
+
2. Grant BigQuery Data Viewer (`roles/bigquery.dataViewer`) on the datasets the
|
|
2886
|
+
service account may query. Dataset grants restrict data access separately
|
|
2887
|
+
from project-level job execution. Grant further permissions only for the
|
|
2888
|
+
operations you intend to expose; OAuth scopes do not replace IAM.
|
|
2889
|
+
[BigQuery IAM roles and grant levels](https://docs.cloud.google.com/bigquery/docs/access-control).
|
|
2890
|
+
Create a service-account JSON key and store it as
|
|
2891
|
+
`GOOGLE_SERVICE_ACCOUNT_JSON` in the deployment secret store; the helper
|
|
2892
|
+
does not use ADC, metadata-server credentials, or workload identity federation.
|
|
2893
|
+
[Service-account key creation](https://docs.cloud.google.com/iam/docs/keys-create-delete).
|
|
2894
|
+
3. For the personal connection, configure Google Auth Platform branding,
|
|
2895
|
+
audience and data access. Choose **Internal** for an application restricted to
|
|
2896
|
+
people in your Workspace organization, using a project belonging to that
|
|
2897
|
+
organization. Internal consent does not grant dataset access; each person
|
|
2898
|
+
still needs the applicable IAM permissions.
|
|
2899
|
+
[Configure consent and audience](https://developers.google.com/workspace/guides/configure-oauth-consent).
|
|
2900
|
+
4. Create a **Web application** OAuth client under Google Auth Platform → Clients.
|
|
2901
|
+
Register the exact Connecta callback, for example
|
|
2902
|
+
`https://connecta.example/oauth/callback/warehouse_personal`, using the
|
|
2903
|
+
deployment's configured public origin. Store its client ID and secret as
|
|
2904
|
+
deployment secrets. Google's hosted MCP servers do not support DCR or client
|
|
2905
|
+
metadata documents, so configure the static `auth.client` shown above.
|
|
2906
|
+
[Web OAuth client setup](https://docs.cloud.google.com/mcp/set-up-authentication-mcp-servers#web),
|
|
2907
|
+
[Google MCP registration limitations](https://docs.cloud.google.com/mcp/authenticate-mcp#limitations).
|
|
2908
|
+
|
|
2909
|
+
The pre-registered client path supports Google's exact issuer and
|
|
2910
|
+
`client_secret_post`; Google's [discovery document](https://accounts.google.com/.well-known/openid-configuration)
|
|
2911
|
+
advertises that method, PKCE S256, and `https://oauth2.googleapis.com/token`.
|
|
2912
|
+
The personal example requests `access_type=offline` to obtain a refresh token
|
|
2913
|
+
and `prompt=consent` to show consent again when reconnecting. See
|
|
2914
|
+
[Google's offline-access requirements](https://developers.google.com/identity/protocols/oauth2/web-server#offline).
|
|
2915
|
+
Connecta retains the stored refresh token when a successful refresh response
|
|
2916
|
+
omits `refresh_token`, allowing later refreshes with that same token. After
|
|
2917
|
+
adding these parameters to an existing connector, Disconnect and reconnect.
|
|
2918
|
+
Live Google consent and project-specific permissions still need verification
|
|
2919
|
+
against the operator's project.
|
|
2920
|
+
|
|
2616
2921
|
## Contributor reference
|
|
2617
2922
|
|
|
2618
2923
|
### INV-5 output redaction limits
|
|
@@ -65,9 +65,15 @@ createConnecta({ executor: workerExecutor({ loader: env.LOADER }), readOnly: fal
|
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
The adapter uses only `loader` and an optional `timeout`. Its admission defaults
|
|
68
|
-
are two active runs, eight queued runs,
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
are two active runs, eight queued runs, a 60-second queue deadline (the default
|
|
69
|
+
guest deadline) and a one-second `retryAfterMs` hint. `admission` replaces all
|
|
70
|
+
four: `{ concurrency, maxQueueSize, queueTimeoutMs, retryAfterMs? }`, where an
|
|
71
|
+
omitted `retryAfterMs` equals `queueTimeoutMs`. The pool belongs to the executor
|
|
72
|
+
instance, so a Worker that builds one per isolate bounds each isolate, not the
|
|
73
|
+
deployment or a caller: concurrent requests that land on one isolate share its
|
|
74
|
+
slots, and requests on other isolates do not count. Lease release disposes RPC
|
|
75
|
+
and loader handles independently of guest settlement. `bindings`, `modules`, or
|
|
76
|
+
`globalOutbound` violate `P2`.
|
|
71
77
|
The [Worker example](../examples/worker/README.md#code-mode) has the full setup.
|
|
72
78
|
|
|
73
79
|
Keep the Worker's optional peer. Unbranded executors throw, including bundled upstream copies.
|
|
@@ -981,7 +987,21 @@ payload is refused before parsing and names no address.
|
|
|
981
987
|
plus a bounded queue with a wait timeout. Overload is a retryable
|
|
982
988
|
`executor_overloaded` carrying `retryAfterMs`; cancellation and shutdown are
|
|
983
989
|
terminal. Admission happens _before_ any catalog or provider is built, so a
|
|
984
|
-
queued request holds no state.
|
|
990
|
+
queued request holds no state. A full queue refuses at once with
|
|
991
|
+
`Executor queue is full.`; a queued run that outlives `queueTimeoutMs` is
|
|
992
|
+
refused with `Executor admission timed out after <n>ms.`. Either arrives as an
|
|
993
|
+
error result whose `structuredContent.error` is
|
|
994
|
+
`{ code: "executor_overloaded", message, retryable: true, retryAfterMs }`, with
|
|
995
|
+
`hostCalls` all zero; the program never started, so retrying it after
|
|
996
|
+
`retryAfterMs` sends nothing twice. Whichever request releases a slot, the
|
|
997
|
+
queued run resumes, builds its providers, makes its host calls and holds its
|
|
998
|
+
sandbox handles in its own request; the releasing request's result is
|
|
999
|
+
unaffected. A slot handed to a queued run is heard a turn after it is granted.
|
|
1000
|
+
When the executor closes in that gap, the slot is given back and the run is
|
|
1001
|
+
refused with `executor_closed`, so nothing granted before `close()` starts after
|
|
1002
|
+
it. A grant that beat the caller's abort stays the caller's, which releases it
|
|
1003
|
+
on arrival. A queued run also holds its MCP request permit
|
|
1004
|
+
([request admission](./architecture.md#admission-in-two-places)) while it waits.
|
|
985
1005
|
|
|
986
1006
|
**L8.** Result, log, host-call, and emission budgets are deployment configuration.
|
|
987
1007
|
A call may request `timeoutMs` just as a direct call does; the guest deadline is
|
|
@@ -1287,7 +1307,7 @@ rejection, and branded adapter acceptance across module copies.
|
|
|
1287
1307
|
| `L1`, `L2` | `test/guest-api-contract.test.ts` (in-flight call fails `cancelled`), `test/execute.test.ts` (cancels outstanding host calls, discovery included, and refuses discovery after the run; a cancelled wedged executor, or one whose `acquire()` ignores the signal, returns promptly and releases its lease) |
|
|
1288
1308
|
| `L3`, `X1` | `test/guest-api-contract.test.ts` (short-deadline executors), `test/execute.test.ts` (the watchdog ends a never-settling executor, frees the default pool, spares a slow run, and falls back from an unusable value) |
|
|
1289
1309
|
| `L4`, `L8` | `test/guest-api-contract.test.ts`, `test/execute.test.ts` (shared discovery/call budgets and terminal catch-and-continue loops and queued-write cancellation on both executors), `test/worker-budget-response.node.test.ts` (native handle disposal and admission recovery across completed HTTP responses) |
|
|
1290
|
-
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue)
|
|
1310
|
+
| `L5`, `L7`, `X2` | `test/quickjs-executor.node.test.ts` (CPU, heap), `test/execute.test.ts` and `test/executor-admission.test.ts` (bounded admission and queue), `test/executor-requests.test.ts` (queued handoffs and refusal shape across separate workerd requests), `test/request-context.node.test.ts` (queued and QuickJS host-call request context) |
|
|
1291
1311
|
| `L6`, `X10` | `test/guest-api-contract-quickjs.node.test.ts` and `test/guest-api-contract.test.ts` (whole oversized reads and one-write pageable handles), `test/sqlite-storage.node.test.ts` and `test/kv-to-d1.test.ts` (SQL stash and native D1 fault probes), `test/quickjs-executor.node.test.ts` (bridge and IPC bounds for arguments and result; the address in the over-bound message), `test/quickjs-child-stderr.node.test.ts` (outer reply serialization failure settles the call) |
|
|
1292
1312
|
| `V1`–`V4` | `test/guest-api-contract.test.ts` (dispatched calls, every refusal class including an address no connector owns, the friction each derives, no event for the execution itself), `test/activity.test.ts` (the shared code → friction table, the identity clamp, the one-attempt floor), `test/browser/operator-ui.spec.ts` (identity changes), `test/sql-storage-contract.ts`, run by `test/sqlite-storage.node.test.ts` (historical activity rows still round-trip) |
|
|
1293
1313
|
| `V5`, `W9` | `test/program-writes.test.ts` (unawaited program drain, failed-program accounting), `test/identity-write-journey.node.test.ts` (committed ambiguity, reconciliation and physical writes), `test/invocation-pipeline.test.ts` (the gate after validation, an unrecorded refusal) |
|