@zackbart/connecta 0.22.3 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/AGENTS.md +5 -0
  2. package/CHANGELOG.md +82 -0
  3. package/README.md +19 -11
  4. package/dist/activity-friction.d.ts +3 -0
  5. package/dist/activity-friction.js +19 -0
  6. package/dist/activity.d.ts +11 -2
  7. package/dist/activity.js +15 -19
  8. package/dist/auth/downstream-oauth.d.ts +2 -1
  9. package/dist/auth/downstream-oauth.js +10 -1
  10. package/dist/branding.d.ts +67 -0
  11. package/dist/branding.js +176 -0
  12. package/dist/catalog-service.d.ts +1 -6
  13. package/dist/catalog-service.js +3 -53
  14. package/dist/connectors/remote-mcp.js +3 -5
  15. package/dist/credential-contract.d.ts +24 -0
  16. package/dist/credential-contract.js +1 -0
  17. package/dist/credential-rules.d.ts +85 -0
  18. package/dist/credential-rules.js +107 -0
  19. package/dist/credentials.d.ts +4 -100
  20. package/dist/credentials.js +3 -107
  21. package/dist/errors.d.ts +1 -1
  22. package/dist/execute.d.ts +5 -52
  23. package/dist/execute.js +40 -347
  24. package/dist/executors/quickjs-protocol.d.ts +0 -7
  25. package/dist/executors/quickjs-protocol.js +2 -10
  26. package/dist/executors/quickjs.js +1 -1
  27. package/dist/index.d.ts +23 -56
  28. package/dist/index.js +30 -58
  29. package/dist/invocation.d.ts +0 -33
  30. package/dist/invocation.js +56 -124
  31. package/dist/meta-tools.d.ts +7 -6
  32. package/dist/meta-tools.js +14 -21
  33. package/dist/module-contracts.d.ts +19 -0
  34. package/dist/module-contracts.js +1 -0
  35. package/dist/operator-ui/generated.js +2 -2
  36. package/dist/operator-ui/model.d.ts +6 -3
  37. package/dist/operator-ui/view.d.ts +2 -18
  38. package/dist/operator-ui/view.js +3 -20
  39. package/dist/registry.d.ts +4 -1
  40. package/dist/registry.js +8 -6
  41. package/dist/routes/activity.js +1 -1
  42. package/dist/routes/credentials.js +5 -2
  43. package/dist/routes/mcp.js +7 -51
  44. package/dist/routes/oauth-management.d.ts +2 -0
  45. package/dist/routes/oauth-management.js +108 -0
  46. package/dist/routes/oauth.d.ts +0 -1
  47. package/dist/routes/oauth.js +21 -121
  48. package/dist/routes/shared.d.ts +19 -17
  49. package/dist/routes/shared.js +48 -44
  50. package/dist/routes/ui.js +36 -33
  51. package/dist/server.d.ts +1 -2
  52. package/dist/server.js +7 -45
  53. package/dist/skills.d.ts +1 -1
  54. package/dist/skills.js +55 -19
  55. package/dist/types.d.ts +3 -15
  56. package/dist/ui.d.ts +15 -70
  57. package/dist/ui.js +176 -317
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/documentation/architecture.md +31 -26
  61. package/documentation/auth.md +65 -111
  62. package/documentation/call-admission.md +10 -11
  63. package/documentation/cloudflare.md +1 -1
  64. package/documentation/code-mode.md +65 -260
  65. package/documentation/connectors.md +8 -6
  66. package/documentation/linear.md +1 -1
  67. package/documentation/meta-tools.md +37 -18
  68. package/documentation/mixpanel.md +1 -1
  69. package/documentation/notion.md +2 -2
  70. package/documentation/operations.md +19 -23
  71. package/documentation/operator-ui.md +82 -104
  72. package/documentation/optional-modules-upgrade.md +243 -0
  73. package/documentation/provider-conventions.md +7 -5
  74. package/documentation/revenuecat.md +1 -1
  75. package/documentation/storage-and-credentials.md +59 -40
  76. package/documentation/stripe.md +1 -1
  77. package/documentation/upgrading.md +83 -8
  78. package/ethos.md +36 -43
  79. package/examples/worker/AGENTS.md +3 -1
  80. package/examples/worker/README.md +68 -84
  81. package/examples/worker/src/d1-activity.ts +1 -1
  82. package/examples/worker/src/index.ts +11 -6
  83. package/package.json +18 -2
  84. package/templates/node/AGENTS.md +8 -6
  85. package/templates/node/README.md +56 -67
  86. package/templates/node/package.json +1 -1
  87. package/templates/node/src/file-activity.ts +1 -1
  88. package/templates/node/src/index.ts +11 -12
  89. package/dist/access-tokens.d.ts +0 -31
  90. package/dist/access-tokens.js +0 -236
  91. package/dist/apps-shell.d.ts +0 -37
  92. package/dist/apps-shell.js +0 -174
  93. package/dist/routes/access-tokens.d.ts +0 -6
  94. package/dist/routes/access-tokens.js +0 -83
package/dist/version.js CHANGED
@@ -4,4 +4,4 @@
4
4
  * a bump that forgets this file fails the build rather than shipping a stale
5
5
  * version to `/health` and to downstream MCP handshakes.
6
6
  */
7
- export const CONNECTA_VERSION = "0.22.3";
7
+ export const CONNECTA_VERSION = "0.24.0";
@@ -66,24 +66,21 @@ read top to bottom.
66
66
  | --- | --- | --- |
67
67
  | 0 | HTTPS upgrade | 308 to `publicUrl` when it is HTTPS and the request arrived over HTTP. Path and query are *assigned* onto the configured URL, never resolved against it, so a `//host` pathname cannot replace the deployment origin. `/health` is exempt: a loopback container probe must not depend on public DNS and TLS. `/ui` is canonicalized to `/` while upgrading. |
68
68
  | 0 | Cloudflare Access (Worker deployment, when enabled) | Edge admission before this route table. Managed OAuth owns its challenge and discovery metadata; an admitted direct invocation carries trusted identity in `ctx.access`. |
69
- | 1 | `/ui/access-tokens[/<id>]`, `/ui/credentials/<id>[/<action>]`, `/ui/oauth/<id>` | Private mutation routes, matched **first** so nothing can shadow them and so they own their own `OPTIONS` they answer it with a refusal rather than inheriting the wildcard CORS preflight. |
70
- | 2 | `OPTIONS` | Each auth provider's `handleMetadata` gets a chance (CORS preflight for browser MCP clients); otherwise 204 with MCP CORS. |
71
- | 3 | `/.well-known/*` | Auth providers' `handleMetadata`, open. 404 when none handles it. |
72
- | 4 | `/health` | Open JSON: status, connector count, `serverInfo`, the configured executor's sanitized name when it has one, catalog-drift counts, admission snapshots, reserved route names, and `deployment` when `deploymentInfo` is set. Payload-free by construction, and it never joins the MCP queue. |
73
- | 5 | `/oauth/callback/<connectorId>` | Downstream-OAuth completion, open, `verifyState` before `finishAuth`. Personal flows first resolve the short-lived state hash to the principal partition. |
74
- | 6 | `/favicon.*`, `/ui` → `/`, the operator shells, `/ui/data` | The operator surface ([operator UI](./operator-ui.md)). The shells are open and data-free; `/ui/data` behind them is gated. Built-ins are matched before connector routes, so a connector cannot shadow a page. |
75
- | 7 | `/ui/activity` | Gated, plus the optional `activity.readGate`. `GET` only; 404 with no `activity.store.list`. |
76
- | 8 | `/mcp` | **Admission before auth**, then the auth gate, then a fresh MCP server. |
77
- | 9 | connector `handleRequest` | Registration order, open. Dispatched only after every built-in misses, so a connector can *add* a route and never shadow one of connecta's. First non-null response wins; a throw is a 500, not a fall-through. |
78
- | 10 | — | 404. |
69
+ | 1 | Mounted UI routes | The optional UI handles its shells, assets, data, details, and auth mutations before wildcard OPTIONS. Mutation routes refuse preflight rather than inheriting MCP CORS. No UI module means none of these routes. |
70
+ | 2 | `OPTIONS` | Auth metadata gets a chance, otherwise MCP CORS preflight. |
71
+ | 3 | `/.well-known/*` | Auth metadata, or 404. |
72
+ | 4 | `/health` | Open payload-free health, executor, admission, and deployment metadata; reserved routes reflect installed modules. |
73
+ | 5 | `/oauth/callback/<connectorId>` | Core downstream OAuth completion, state verification and personal ownership checks; independent of UI. |
74
+ | 6 | `/mcp` | Admission before auth, then a request-local MCP server. |
75
+ | 7 | Other paths | 404. Custom HTTP routes belong to the deployment. |
76
+
79
77
 
80
78
  Every response leaves through `withSecurityHeaders`: `nosniff`, a no-referrer
81
- policy, HSTS on HTTPS, and — on the operator shells a nonce-based script CSP
82
- and framing denial. `test/server-route-contracts.test.ts` pins this ordering
79
+ policy, HSTS on HTTPS, while the UI module adds a nonce-based script CSP and framing denial to its shells. `test/server-route-contracts.test.ts` pins this ordering
83
80
  and the exact refusal bodies; it exists because the ordering is invisible in
84
81
  any one file and a reordering reads like a harmless refactor.
85
82
 
86
- `/mcp` itself is four steps, in this order and for these reasons:
83
+ `/mcp` itself is five steps, in this order and for these reasons:
87
84
 
88
85
  1. **Admit.** One permit from the deployment-wide FIFO pool, taken before auth
89
86
  so an unauthenticated flood costs a permit rather than a Clerk lookup
@@ -102,8 +99,7 @@ any one file and a reordering reads like a harmless refactor.
102
99
  than silently serving the full registry. Retiring a scoping boundary into
103
100
  fail-open is the one outcome worse than the 404.
104
101
  5. **Serve.** A fresh `McpServer` per request, the seven meta-tools registered
105
- against the registry, the Apps shell resource registered (and
106
- `resources/list` deliberately answering with nothing), and the response
102
+ against the registry and the response
107
103
  handed back.
108
104
 
109
105
  ## Layers below the meta-tools
@@ -115,7 +111,7 @@ owns or hands out, and a change usually belongs in exactly one of them:
115
111
  | --- | --- |
116
112
  | `src/registry.ts` | The connector set, identity-scoped views, personal storage partitions, address resolution, catalog TTL/persistence/completeness, refresh single-flight, connector health, per-connector call limiters, and drift. Construction-time refusals live here. |
117
113
  | `src/catalog-service.ts` | Request-local tool listing, search, and describe. It coalesces reads inside one request and opts agent reads into the runtime's deferred catalog channel when one exists. |
118
- | `src/invocation.ts` | One tool call: argument validation, call admission, per-attempt timeout, retry with the connector's own `Retry-After` honoured exactly or declined, result unwrapping, size capping, and the activity record. |
114
+ | `src/invocation.ts` | One tool call: argument validation, call admission, one-attempt timeout, provider retry hints, result unwrapping, size capping, and the activity record. |
119
115
  | `src/catalog.ts` | Ranking, description summarizing, and the compact schema renderer discovery shows. |
120
116
  | `src/result-shapes.ts` | Bounded runtime-only inference and merging for output shapes learned from successful read-only calls whose providers declared none. |
121
117
 
@@ -127,6 +123,19 @@ read-only check. `test/execute.test.ts` asserts that parity directly, because
127
123
  the alternative — a sandbox path that quietly diverges — is how generated code
128
124
  would mint a capability.
129
125
 
126
+ ## Optional deployment modules
127
+
128
+ `createConnecta` takes closed typed `ui`, `vault`, and `activity` slots. Factories
129
+ live at `/ui`, `/credentials`, and `/activity`; bearer auth lives at
130
+ `/auth/bearer`. Root exports the contracts, never these implementations. There
131
+ is no module array, runtime registration, or plugin lifecycle.
132
+
133
+ Core keeps connector discovery, the executor contract, invocation, permissions,
134
+ and OAuth callback verification together. Optional modules contribute no
135
+ runtime work when omitted. The UI supplies credential handoff URLs only while
136
+ mounted. Status reads never initiate OAuth, and each lazy details request owns
137
+ its downstream scope. See [operator UI](./operator-ui.md).
138
+
130
139
  ## Import-graph purity
131
140
 
132
141
  Nothing reachable from `src/index.ts` may import a `node:` builtin. The core is
@@ -144,8 +153,8 @@ specific to a direct Worker invocation carrying `ctx.access`.
144
153
 
145
154
  `test/purity.test.ts` walks the relative-import graph from `src/index.ts` and
146
155
  fails on (a) any `node:` specifier in a reachable file and (b) the Node
147
- adapter, file storage, QuickJS parent or child, or the Clerk adapter being
148
- reachable at all. `test/package-surface.test.ts` and
156
+ adapter, file storage, QuickJS parent or child, auth adapters, UI bundle,
157
+ encrypted vault implementation, or activity implementation being reachable at all. `test/package-surface.test.ts` and
149
158
  `scripts/check-package.mjs` guard the other half — that the published tarball
150
159
  matches the same boundary.
151
160
 
@@ -162,8 +171,7 @@ src/
162
171
  server.ts route ordering, HTTPS upgrade, security wrapper
163
172
  routes/ one file per surface; shared.ts holds the auth gate
164
173
  meta-tools.ts the six non-execute meta-tools over the registry
165
- execute.ts execute_code, the sandbox host bridge, emit and ui
166
- apps-shell.ts the one build-time MCP Apps template
174
+ execute.ts execute_code, the sandbox host bridge, emitted media
167
175
  skills.ts MCP instructions, the usage skill, connector guides
168
176
  registry.ts connector set, addresses, health, call limiters
169
177
  catalog-service.ts request-local catalog access, search, and describe
@@ -172,8 +180,7 @@ src/
172
180
  invocation.ts one tool call, end to end
173
181
  catalog-drift.ts vetted manifests and the counts a refresh produces
174
182
  credentials.ts the AES-GCM connector vault over KVStorage
175
- access-tokens.ts operator-issued MCP bearer tokens
176
- activity.ts payload-free event contracts + best-effort recorder
183
+ activity.ts optional history factory and best-effort recorder
177
184
  call-admission.ts connector-partitioned downstream permits and budgets
178
185
  executor-admission.ts the portable bounded queue both pools use
179
186
  ui.ts the served operator shell and /ui/data payload
@@ -194,8 +201,7 @@ src/
194
201
  authenticated subject. Anything cached per request still dies with it.
195
202
  Putting a downstream client or credential on the wrong side of those lines
196
203
  is the highest-severity mistake available here.
197
- - **Route order is behavior.** Moving a built-in below the connector dispatch
198
- hands a connector the ability to shadow it. Moving a mutation route below the
204
+ - **Route order is behavior.** Moving a mutation route below the
199
205
  wildcard `OPTIONS` opts it into CORS preflight.
200
206
  - **Admission runs before auth, on purpose.** Reordering them to "authenticate
201
207
  first" makes the cheapest possible attack the most expensive request.
@@ -203,8 +209,7 @@ src/
203
209
  the connector limiters, then the executor. Node's `listen()` calls it on
204
210
  SIGTERM/SIGINT.
205
211
  - **Structural mistakes throw at construction.** A duplicate connector id, an
206
- invalid admission rule, `accessTokens` without an interactive operator provider, a missing
207
- executor: all refuse to boot. A deployment that starts in the wrong shape is
212
+ invalid admission rule, removed `accessTokens` option, or missing executor: all refuse to boot. A deployment that starts in the wrong shape is
208
213
  worse than one that does not start.
209
214
 
210
215
  ## Tests that enforce this
@@ -1,67 +1,60 @@
1
1
  # Inbound auth
2
2
 
3
- Inbound auth decides who may reach the MCP endpoint. A deployment may admit a
4
- static bearer, operator-issued access tokens, Clerk identities, Cloudflare
5
- Access identities on Workers, or a mixture. Static bearers are checked first;
6
- the remaining providers keep configuration order. The first successful
7
- identity owns the activity actor for that request.
3
+ Inbound auth decides who may reach the MCP endpoint. Import configured bearer
4
+ support from `@zackbart/connecta/auth/bearer`, Clerk from `/auth/clerk`, or
5
+ Cloudflare Access from `/auth/cloudflare-access`. Providers may be combined;
6
+ static bearers are checked first, then other providers in configuration order.
7
+ Connecta no longer issues `cta_` tokens or serves token-management routes.
8
8
 
9
9
  ## Principals, visibility, and operators
10
10
 
11
- Connecta distinguishes three identities. The actor is the exact caller written
12
- to activity. The subject is any stable authenticated caller and owns transient
13
- results such as `get_result` pages. The principal is the human owner of personal
11
+ The actor identifies the caller in activity. The subject owns transient results
12
+ such as `get_result` pages. The principal is the human owner of personal
14
13
  connector auth. An interactive Clerk or Access user supplies all three. A
15
- Cloudflare service identity has an actor and subject but no principal. A
16
- connecta access token has its own actor and subject and inherits the principal
17
- that created it, so agents using that token reach the creator's personal
18
- connections without becoming operators.
19
-
20
- `identity.connectorAccess` derives the connector ids a caller may discover and
21
- invoke. The resolver receives authenticated identity data, never request input,
22
- and returns `"all"` or a list of ids declared in `connectors`. An unknown id or
23
- a thrown resolver fails the request closed.
24
-
25
- `identity.connectorAccess` is also the credential-management boundary. A
26
- signed-in human may save, test, disconnect, and authorize every visible
27
- connector: personal auth changes only that principal's partition, while shared
28
- auth changes the deployment-wide grant for everyone who can see the connector.
29
- Use `authScope: "personal"` when one member must not rotate another member's
30
- connection.
31
-
32
- `identity.operatorAccess` separately reserves deployment-wide administration:
33
- access-token creation and global activity history. Omit the resolver to
34
- preserve the prior rule that every interactive human is an operator. When it is
35
- configured, activity history is operator-only because its global event stream
36
- contains other principals' connector names and actors.
14
+ Cloudflare service identity has an actor and subject but no principal.
15
+
16
+ `identity.connectorAccess` returns `"all"` or declared connector ids. It governs
17
+ discovery and use, and defaults to all connectors. Visibility alone grants no
18
+ authentication-management permission. Two independent resolvers return
19
+ `"all"`, `"none"`, or declared connector ids:
20
+
21
+ - `credentialAdministration` allows an interactive human to manage shared
22
+ credentials and shared OAuth grants.
23
+ - `personalConnection` allows an interactive human to manage their own
24
+ credentials and OAuth grants on personal connectors.
25
+
26
+ Both default to `"none"`. Each action requires visibility and the relevant
27
+ permission. Personal actions also require a stable namespaced principal and
28
+ always use that principal's partition. Resolver exceptions and unknown ids
29
+ fail closed. Permissions come from authenticated identity, never caller input.
30
+
31
+ The management resolvers receive `Readonly<AuthenticatedIdentity>`.
32
+ `identity.activityAccess` receives `Readonly<IdentityReference>` with `id` and
33
+ `namespace`, and controls reading global activity. Its default admits
34
+ interactive humans, so team deployments should set it explicitly if the event
35
+ stream should be restricted. It replaces `operatorAccess`; there is no general
36
+ administrator role or token-management authority.
37
37
 
38
38
  ```ts
39
39
  createConnecta({
40
40
  auth: cloudflareAccessAuth(),
41
41
  identity: {
42
42
  connectorAccess: ({ principal }) =>
43
- principal?.id === "user_a"
44
- ? ["shared_docs", "personal_linear"]
45
- : ["shared_docs"],
46
- operatorAccess: ({ id }) => id === "user_a",
43
+ principal?.id === "owner-id" ? "all" : ["shared_docs", "personal_linear"],
44
+ credentialAdministration: ({ principal }) =>
45
+ principal?.id === "owner-id" ? "all" : "none",
46
+ personalConnection: () => ["personal_linear"],
47
+ activityAccess: ({ id }) => id === "owner-id",
47
48
  },
48
- connectors: [
49
- remoteMcp("shared_docs", { url: "https://example.com/mcp" }),
50
- remoteMcp("personal_linear", {
51
- url: "https://mcp.linear.app/mcp",
52
- authScope: "personal",
53
- auth: { type: "oauth" },
54
- }),
55
- ],
49
+ connectors,
56
50
  executor,
57
51
  });
58
52
  ```
59
53
 
60
- Identity namespaces matter. Built-in Clerk and Access providers supply one.
61
- A custom interactive provider must set `activityActorNamespace` before its
62
- users can own personal auth. It may still use the legacy operator behavior
63
- without one, but connecta will not merge unnamespaced users into personal
64
- storage.
54
+ Built-in Clerk and Access providers supply identity namespaces. A custom
55
+ interactive provider must set `activityActorNamespace` before its users can
56
+ own personal auth. Keep the namespace and principal ids stable across upgrades;
57
+ changing them selects different personal storage partitions.
65
58
 
66
59
  ## Cloudflare Access on Workers
67
60
 
@@ -92,12 +85,11 @@ also means it is deliberately not a Node or `cloudflared` origin adapter, and
92
85
  it does not survive a Service Binding hop: those shapes need their own explicit
93
86
  trust boundary.
94
87
 
95
- A human identity gets MCP and personal-connection access. It gets operator
96
- access unless `identity.operatorAccess` says otherwise. A Cloudflare service-token
97
- identity gets MCP access and a stable activity subject, but no `userId`, so it
98
- cannot write credentials, run downstream OAuth mutations, or issue connecta
99
- tokens. Access policy decides who reaches the Worker; connecta does not mirror
100
- email domains, groups, or device posture into a second policy layer.
88
+ A human identity gets a code-derived MCP view. Managing connection auth requires
89
+ an explicit `credentialAdministration` or `personalConnection` grant. An Access
90
+ service identity has no human principal and cannot mutate connection auth.
91
+ Access decides admission and identity; Connecta configuration selects connector
92
+ access and these narrower permissions.
101
93
 
102
94
  Protect the Worker with a Worker-level Access application whose destination is
103
95
  `{ "type": "worker", "worker_id": "<the Worker script tag>" }`. A traditional
@@ -133,14 +125,12 @@ through the
133
125
 
134
126
  Worker-level Access runs before every connecta route. Consequently:
135
127
 
136
- - `/health`, operator pages, downstream OAuth callbacks, connector-owned
137
- routes, and `/mcp` all require Access unless a more-specific hostname/path
128
+ - `/health`, operator pages, downstream OAuth callbacks, and `/mcp` all require Access unless a more-specific hostname/path
138
129
  policy says otherwise;
139
- - a static connecta bearer and a `cta_…` token are not standalone edge
140
- credentials, because Cloudflare rejects them before connecta sees them; and
141
- - a connector that intentionally exposes a public webhook needs a
142
- more-specific Access application and bypass policy. Do not bypass connecta's
143
- OAuth discovery paths when Managed OAuth is enabled.
130
+ - a static Connecta bearer is not a standalone edge credential, because Cloudflare rejects them before connecta sees them; and
131
+ - custom public webhooks belong to the deployment outside Connecta and need
132
+ their own Access routing policy. Keep Connecta's OAuth discovery paths
133
+ protected when Managed OAuth is enabled.
144
134
 
145
135
  The [Worker example](../examples/worker/) carries the complete deployment shape
146
136
  and the [upgrade guide](./upgrading.md#0200--0212) gives the reversible Clerk
@@ -157,57 +147,21 @@ publishable slot, and a startup error is a log line. A deployment that builds
157
147
  per request, as the Workers shape does, sees the same error on its first
158
148
  request instead of a base64 stack on every route.
159
149
 
160
- ## Operator-issued access tokens
150
+ ## Human authentication management
161
151
 
162
- Set `accessTokens: {}` to let eligible interactive operators create named Bearer
163
- tokens at `/tokens`:
152
+ Credential and OAuth mutation require an admitted interactive human, connector
153
+ visibility, the appropriate shared or personal permission, and an exact
154
+ same-origin `Origin` for browser requests. A configured MCP bearer never becomes
155
+ a browser management credential.
164
156
 
165
- ```ts
166
- createConnecta({
167
- storage,
168
- auth: clerkAuth({ /* ... */ }),
169
- accessTokens: {},
170
- connectors,
171
- });
172
- ```
157
+ With `ui: operatorUi()` and a vault, static credential recovery can return a
158
+ secret-free handoff to the connection UI. Without the UI, that recovery is
159
+ `unavailable`; Connecta does not return a link to a missing page. An authorized
160
+ interactive MCP caller can still start downstream OAuth through
161
+ `authorize_connector` without the UI. Core owns the callback and verifies state
162
+ and principal ownership independently of the optional browser application.
173
163
 
174
- The storage adapter must implement `list(prefix)`. Connecta returns each
175
- `cta_…` secret once and stores only its SHA-256 digest plus non-secret metadata.
176
- The operator can rename or revoke a token later. Revocation removes admission
177
- before updating its display metadata, so a partial storage failure fails
178
- secure.
179
-
180
- Each token has an immutable ID. Activity records store that ID and resolve its
181
- current friendly name only while an authorized operator reads activity.
182
- Revoked records remain as metadata tombstones so historical calls keep their
183
- friendly attribution. New tokens also retain the creating principal. Their MCP
184
- requests use that principal's connector visibility and personal auth while the
185
- token itself remains the activity actor and result owner.
186
-
187
- Access tokens authenticate MCP clients; they are never operator credentials.
188
- Creation, rename, and revocation require the same eligible human identity and
189
- same-origin mutation boundary as connector credentials. `maxActive` defaults
190
- to 100 and can be set from 1 through 1,000.
191
-
192
- Issuance and revocation inherit the consistency guarantees of the configured
193
- storage adapter. Use strongly consistent storage when either change must take
194
- effect globally without a convergence window.
195
-
196
- Human credential mutation is a separate, narrower boundary. The
197
- `/credentials` shell contains no secret data before authentication, and the
198
- mutation API requires same-origin requests from an admitted interactive human.
199
- That human may mutate only visible connector slots. An MCP bearer is never
200
- treated as a browser credential, even when it can call every connector.
201
-
202
- This split is visible in recovery:
203
-
204
- - a bearer-authenticated agent may receive `recovery: "operator_config"` and
205
- pass its `operatorUrl` to a human;
206
- - an interactive human with connector access opens that URL, signs in, and updates the
207
- credential; and
208
- - a bearer-only deployment still returns the handoff honestly, but mutation
209
- remains unavailable until interactive user auth is configured.
210
-
211
- See [meta-tools](./meta-tools.md#authorization-recovery) for the stable recovery
212
- envelope and [storage and credentials](./storage-and-credentials.md) for vault
213
- rules.
164
+ See [meta-tools](./meta-tools.md#authorization-recovery) and
165
+ [storage and credentials](./storage-and-credentials.md). The
166
+ [upgrade guide](./upgrading.md#0240-optional-modules) covers moving clients
167
+ off removed Connecta-issued tokens before changing deployment configuration.
@@ -1,7 +1,7 @@
1
1
  # Downstream call admission
2
2
 
3
3
  One admitted MCP request can fan out into many downstream calls. A program
4
- calls tools from loops, from `connecta.batch`, and from branches the caller
4
+ calls tools from loops, from parallel `connecta.call` calls, and from branches the caller
5
5
  never saw. The deployment-wide request pool
6
6
  ([request admission](./request-admission.md)) bounds the envelope; it cannot
7
7
  see anything inside it. A provider that publishes "60 requests per minute"
@@ -76,16 +76,15 @@ not be a way to reset a live budget. Exhausted partition capacity is
76
76
 
77
77
  The registry owns the limiter, and `InvocationService.invoke` acquires a permit
78
78
  immediately before `Connector.callTool` and releases it in `finally`. Both call
79
- paths top-level `call_tool` and a program's `connecta.call` or
80
- `connecta.batch` — reach that same seam, so a program cannot buy itself a
79
+ paths, top-level `call_tool` and program calls through `connecta.call`,
80
+ reach that same seam, so a program cannot buy itself a
81
81
  second limit by taking the other route.
82
82
 
83
- - **A retry is another attempt.** It reacquires and can consume another budget
84
- entry, because the provider counts it that way. Backoff never holds a
85
- concurrency permit.
86
- - **A proactive short-window `rate_limited` refusal** participates in the
87
- ordinary retry policy and counts as an attempt. Activity records the final
88
- outcome and the attempt count.
83
+ - **A caller retry is another call.** It reacquires and can consume another
84
+ budget entry. Connecta returns retry hints without waiting or retrying.
85
+ - **A proactive short-window `rate_limited` refusal** returns its retry hint
86
+ without waiting or dispatching. Activity records one failed admission attempt;
87
+ a caller may reissue after the window.
89
88
  - **A queued cancellation consumes nothing.** It is removed from the queue with
90
89
  no rolling-window entry charged.
91
90
  - **Caller cancellation is terminal.** It is non-retryable, releases its
@@ -114,7 +113,7 @@ from "we are throttling ourselves".
114
113
  ## Enforcement scope
115
114
 
116
115
  This is deliberately **per-runtime**. It completely contains fan-out inside one
117
- request, including a wide `connecta.batch` in one Worker isolate. A rolling
116
+ request, including parallel `connecta.call` calls in one Worker isolate. A rolling
118
117
  budget is exact inside one Node process or Worker isolate, and best-effort
119
118
  across isolates, replicas, and restarts.
120
119
 
@@ -139,5 +138,5 @@ typed error code.
139
138
  | Invariant | Suite |
140
139
  | --- | --- |
141
140
  | Independent partitions, exact rolling-window reset and retry, queued cancellation charging no budget, synchronous cancel during partition derivation, validated values snapshotted rather than read from mutable config, bounded partition state and contained `partitionKey` failures, empty and multi-rule policies refused | `test/call-admission.test.ts` (controller) |
142
- | One base-registry limiter shared by direct and program calls, batch bounds with input order preserved, cancellation threading, no dispatch or retry or health poisoning after cancellation, short proactive windows retried without poisoning health, payload-free `/health` aggregates | `test/call-admission.test.ts` (integration, Node + Workers) |
141
+ | One base-registry limiter shared by direct and program calls, promise concurrency with input order preserved, cancellation threading, no dispatch or retry or health poisoning after cancellation, retry hints returned without waiting or poisoning health, payload-free `/health` aggregates | `test/call-admission.test.ts` (integration, Node + Workers) |
143
142
  | Where provider budgets are allowed to come from at all | [provider conventions P12](./provider-conventions.md#p12--declare-an-admission-budget-only-when-the-provider-documents-a-number), [provider audit](https://github.com/zackbart/connecta/blob/main/records/provider-audit.md) |
@@ -135,7 +135,7 @@ Dashboard and API tabs.
135
135
 
136
136
  `verify_api_token` needs no permission beyond the token existing. In legacy
137
137
  mode, `verify_global_api_key` reads `/user` to confirm the email and key pair.
138
- The `/credentials` Test action runs the matching check before storage.
138
+ The connection UI Test action at `/` runs the matching check before storage.
139
139
 
140
140
  Cloudflare rate-limits *authentication failures* aggressively and separately
141
141
  from the global limit: a few requests with a bad token return HTTP 429 with