@zackbart/connecta 0.29.0 → 0.30.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 +67 -0
- package/README.md +123 -134
- package/bin/connecta.mjs +13 -2
- package/bin/version.mjs +1 -1
- package/dist/argument-redaction.js +230 -0
- package/dist/auth-elicitation.js +14 -9
- package/dist/authorization-handoff.js +111 -0
- package/dist/catalog-cache.js +2 -2
- package/dist/catalog-service.js +103 -63
- package/dist/config.d.ts +2 -0
- package/dist/config.js +12 -7
- package/dist/connectors/guarded-fetch.js +3 -3
- package/dist/connectors/negotiation-cache.js +1 -1
- package/dist/downstream-input-context.js +9 -0
- package/dist/downstream-input.js +13 -11
- package/dist/errors.d.ts +36 -12
- package/dist/errors.js +19 -9
- package/dist/execute.js +375 -43
- package/dist/executor-result.js +11 -9
- package/dist/executors/quickjs-child.js +12 -5
- package/dist/executors/quickjs-protocol.js +5 -6
- package/dist/executors/quickjs-runtime.js +6 -1
- package/dist/executors/quickjs.js +20 -4
- package/dist/guest-runtime.js +52 -2
- package/dist/guest-types.d.ts +47 -5
- package/dist/index.d.ts +1 -1
- package/dist/invocation.js +40 -14
- package/dist/meta-output.js +0 -15
- package/dist/meta-tools.js +166 -95
- package/dist/oauth-handoff.js +88 -11
- package/dist/program-result.js +5 -0
- package/dist/program-writes.js +5 -1
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/_shared/request-token.js +83 -0
- package/dist/providers/infisical/index.d.ts +7 -0
- package/dist/providers/infisical/index.js +713 -0
- package/dist/providers/infisical/skill.generated.js +8 -0
- package/dist/registry.d.ts +8 -6
- package/dist/registry.js +134 -61
- package/dist/resource-read.js +4 -1
- package/dist/routes/mcp.js +11 -2
- package/dist/sent-secrets.d.ts +54 -13
- package/dist/sent-secrets.js +770 -231
- package/dist/skills.js +26 -16
- package/dist/sqlite.js +21 -6
- package/dist/storage/keys.js +20 -5
- package/dist/storage/memory.js +9 -4
- package/dist/storage/sql.js +18 -9
- package/dist/types.d.ts +17 -3
- package/dist/usage-guide.js +49 -40
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +2 -1
- package/documentation/README.md +63 -0
- package/documentation/architecture.md +121 -89
- package/documentation/auth.md +138 -5
- package/documentation/code-mode.md +224 -107
- package/documentation/deploying.md +146 -0
- package/documentation/integrating.md +159 -0
- package/documentation/meta-tools.md +135 -30
- package/documentation/operating.md +135 -0
- package/documentation/operator-tests.md +4 -0
- package/documentation/operator-ui.md +132 -0
- package/documentation/provider-migration-0.29.md +5 -0
- package/examples/worker/README.md +7 -0
- package/package.json +7 -1
- package/templates/node/README.md +7 -0
- package/templates/node/package.json +1 -1
|
@@ -9,6 +9,26 @@ first; this guide explains the shape and where each subsystem lives. The surface
|
|
|
9
9
|
[meta-tools](./meta-tools.md), [code mode](./code-mode.md), and
|
|
10
10
|
[inbound auth](./auth.md).
|
|
11
11
|
|
|
12
|
+
## Task routes
|
|
13
|
+
|
|
14
|
+
[Agent index](./README.md) routes endpoint use, deployment, integration, and
|
|
15
|
+
repository maintenance. Read this guide when changing the core or checking an
|
|
16
|
+
implementation boundary. Use these sections for the affected subsystem.
|
|
17
|
+
|
|
18
|
+
| Change or question | Sections |
|
|
19
|
+
| --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| Request handling, transport, cleanup | [Two lifetimes](#the-two-lifetimes), [request lifecycle](#request-lifecycle), [admission](#admission-in-two-places) |
|
|
21
|
+
| Discovery, invocation, classification | [Layers below meta-tools](#layers-below-the-meta-tools), [providers and classification](#providers-and-reviewed-classification) |
|
|
22
|
+
| Storage, credentials, catalogs, paging | [Storage and connectors](#storage-credentials-and-connectors), [errors and records](#errors-and-records) |
|
|
23
|
+
| Config validation and optional features | [Configuration](#configuration), [modules](#optional-deployment-modules), [operator contract](./operator-ui.md#operator-data-contract) |
|
|
24
|
+
| Imports, executors, or Effect | [Purity](#import-graph-purity), [Effect](#effect-inside), [sharp edges](#sharp-edges) |
|
|
25
|
+
| 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) |
|
|
26
|
+
|
|
27
|
+
Source paths and test names in each section identify its implementation and
|
|
28
|
+
regression evidence. Recorded production probes and bundle measurements are
|
|
29
|
+
historical observations tied to their stated date or version, not current
|
|
30
|
+
benchmarks or proof that planned replacements have shipped.
|
|
31
|
+
|
|
12
32
|
## The two lifetimes
|
|
13
33
|
|
|
14
34
|
Almost every bug in this codebase is a lifetime mistake, so the split is worth
|
|
@@ -207,6 +227,13 @@ same credential resolution, and the same fail-closed read-only check.
|
|
|
207
227
|
`test/execute.test.ts` asserts the parity directly, because a sandbox path that
|
|
208
228
|
quietly diverges is how generated code would mint a capability.
|
|
209
229
|
|
|
230
|
+
Program result paging shares `meta-tools.ts`'s direct-call stash envelope and
|
|
231
|
+
reader. `program-result.ts` owns executor-independent byte limits;
|
|
232
|
+
`execute.ts` issues bounded transfer cursors, and `guest-runtime.ts` reconstructs
|
|
233
|
+
values through provider preludes. Cursors hold only an ID and offset for the
|
|
234
|
+
current run. Result bytes, expiry, authority and capacity charges stay in the
|
|
235
|
+
existing storage and ledger; no executor-specific store or storage key is added.
|
|
236
|
+
|
|
210
237
|
## Admission, in two places
|
|
211
238
|
|
|
212
239
|
Request admission (`src/executor-admission.ts`, applied in `src/routes/mcp.ts`)
|
|
@@ -324,11 +351,20 @@ marker refuses stale copies afterward.
|
|
|
324
351
|
|
|
325
352
|
`KVStorage` is `get`/`set`/`delete`/`list(prefix)`/`compareAndSet`, all
|
|
326
353
|
required, and `createConnecta` refuses storage missing one (INV-11).
|
|
354
|
+
Adapters must also declare `capabilities: { absoluteExpiry: true }`.
|
|
355
|
+
Construction rejects missing or false capability declarations with an error
|
|
356
|
+
naming `capabilities.absoluteExpiry`. The memory, SQLite, and D1 adapters carry
|
|
357
|
+
this marker. Custom adapters may declare it only after implementing absolute
|
|
358
|
+
expiry on both writes; a legacy `ttlSeconds`-only adapter is rejected by the
|
|
359
|
+
published types and at runtime.
|
|
327
360
|
`compareAndSet(key, expected, next, options?)` is an atomic claim: `null` means
|
|
328
361
|
absent (expired counts) on the way in and delete on the way out, and a
|
|
329
|
-
successful write takes the same
|
|
330
|
-
created and checked by the database clock inside each statement, so isolate
|
|
331
|
-
clock skew cannot expire a live holder's record.
|
|
362
|
+
successful write takes the same expiry options as `set`. Relative `ttlSeconds`
|
|
363
|
+
is created and checked by the database clock inside each statement, so isolate
|
|
364
|
+
clock skew cannot expire a live holder's record. Absolute `expiresAtMs` is a
|
|
365
|
+
safe-integer epoch-millisecond deadline stored verbatim and checked against the
|
|
366
|
+
storage clock. Supply at most one option. Custom adapters must implement both;
|
|
367
|
+
a delayed absolute-expiry write must not restart a relative TTL at commit time. Each SQL claim
|
|
332
368
|
is one statement, which SQLite executes atomically and D1 serializes on its
|
|
333
369
|
primary. Everything that must claim a key exactly once relies on it with no
|
|
334
370
|
read-then-write fallback: access-token issuance and
|
|
@@ -350,7 +386,8 @@ State-file import validates all keys before writing. The `connecta_kv` table
|
|
|
350
386
|
keeps its existing TEXT keys, including compatibility with the 0.28 schema.
|
|
351
387
|
Storage writes no log lines. A refused import names the file and an entry's
|
|
352
388
|
position, never a key, a value, or the JSON parser's account, which quotes
|
|
353
|
-
the file (INV-6).
|
|
389
|
+
the file (INV-6). Database failures report only a fixed import step and an
|
|
390
|
+
entry's position, including errors raised by target-database triggers.
|
|
354
391
|
|
|
355
392
|
Remote MCP catalogs use the SDK `ResponseCacheStore` over this same SQL KV.
|
|
356
393
|
The `response-cache:v1:` family binds connector id, a hash of its configured
|
|
@@ -433,12 +470,53 @@ Result paging stores each oversized result for 15 minutes, chunked so a page
|
|
|
433
470
|
reads only what it covers. Its bounds (`results.maxStashBytes`,
|
|
434
471
|
`results.maxStashEntries`) are the deployment's, not an isolate's: every charge
|
|
435
472
|
is a row in one ledger record, booked by compare-and-set before any chunk is
|
|
436
|
-
written, so isolates and processes sharing the store see one count.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
473
|
+
written, so isolates and processes sharing the store see one count. Every
|
|
474
|
+
reservation starts with a finite deadline of booking time plus the 30-second
|
|
475
|
+
write budget plus the chunk TTL, normally 15 minutes. Pending writes retain
|
|
476
|
+
that charge until its deadline. Booking retries recognize their own reservation
|
|
477
|
+
ID and reuse its stored deadline, including a final read after an ambiguous
|
|
478
|
+
last CAS. A lost swap or transient storage error backs off and re-reads, up to
|
|
479
|
+
32 attempts. A full ledger or unconfirmed exhausted booking returns no result id.
|
|
480
|
+
|
|
481
|
+
Caller time only nominates charges for pruning. Before discarding one, the
|
|
482
|
+
ledger writes a probe with that charge's absolute deadline and reads it back.
|
|
483
|
+
Only an absent probe proves storage has passed the deadline; a live probe keeps
|
|
484
|
+
the charge, and a storage error cannot free capacity. Each deadline has its own
|
|
485
|
+
key in `stashExpiryKeys`, so concurrent probes cannot overwrite a different
|
|
486
|
+
deadline's proof. Probes expire at the deadline they check and need no cleanup.
|
|
487
|
+
This uses the same absolute-expiry contract on memory, SQLite and D1 without a
|
|
488
|
+
storage API or schema change. An ahead caller clock cannot admit a second stash
|
|
489
|
+
while the first remains readable.
|
|
490
|
+
|
|
491
|
+
The whole chunk-write phase has a 30-second timeout measured from booking, with
|
|
492
|
+
a clock check before and after each write. Trailing chunks precede the header;
|
|
493
|
+
failure or timeout returns no result id and stops the write loop. Chunks use an
|
|
494
|
+
absolute expiry at booking time plus their TTL, so slow writes shorten the
|
|
495
|
+
remaining paging window. Storage promises cannot guarantee cancellation of an
|
|
496
|
+
already-dispatched database operation, and a rejected client response does not
|
|
497
|
+
prove that operation cannot commit later. Even a commit after timeout or process
|
|
498
|
+
interruption keeps that absolute expiry, rather than starting another TTL.
|
|
499
|
+
|
|
500
|
+
Settlement and release only shorten or remove a reservation. Successful writes
|
|
501
|
+
attempt to shorten the charge to the fixed chunk expiry. Cleanup releases it
|
|
502
|
+
only after all deletions succeed and every dispatched write returned confirmed
|
|
503
|
+
success. Failed cleanup or exhausted release attempts finite settlement when
|
|
504
|
+
no write is unconfirmed. A rejected or timed-out write keeps its original
|
|
505
|
+
reservation because it may still commit after deletion. Each completion path
|
|
506
|
+
has one 15-second budget covering deletion, release, fallback settlement, and
|
|
507
|
+
their storage waits and retries. Successful-write settlement has the same
|
|
508
|
+
15-second budget. A stalled operation stops being awaited when the budget
|
|
509
|
+
expires; its late response starts no further I/O, and no background cleanup
|
|
510
|
+
fiber is started. The caller returns its write outcome even if these
|
|
511
|
+
optimizations cannot finish. Ledger updates retry within the same 32-attempt
|
|
512
|
+
budget. If they fail, time out, crash, or exhaust,
|
|
513
|
+
capacity may be over-held until the booked deadline, at most 15 minutes and
|
|
514
|
+
30 seconds for the normal TTL. The ledger row itself has an absolute expiry at
|
|
515
|
+
its latest live reservation deadline. There are no completion receipts or
|
|
516
|
+
separate recovery rows; expiry probes carry no completion state. Expired data reads as absent and normal storage sweeps
|
|
517
|
+
reclaim the physical rows. A full stash returns the successful call's preview
|
|
518
|
+
and a paging-unavailable notice rather than a result id. Shared failure cases
|
|
519
|
+
live in `test/stash-charge-contract.ts`, exercised by the memory and SQL suites.
|
|
442
520
|
|
|
443
521
|
`src/credentials.ts` is the AES-GCM vault behind the root-exported
|
|
444
522
|
`CredentialVault` contract, selected through the `vault` slot. It binds connector
|
|
@@ -488,6 +566,14 @@ credentials produce one payload-free warning per connector; use secrets of
|
|
|
488
566
|
at least eight characters. Encoded and JSON-escaped echoes of registered
|
|
489
567
|
credentials follow the same redaction rules. Connecta's own messages
|
|
490
568
|
never quote a credential.
|
|
569
|
+
Core uncertainty and retry envelopes share one schema-filtered argument snapshot,
|
|
570
|
+
captured before dispatch and byte budgeting. `writeOnly: true` omits values of
|
|
571
|
+
every length, including nested properties, array items and prefixes, local
|
|
572
|
+
`$ref`/`$defs`, and `allOf`. `oneOf`/`anyOf` must agree on sensitivity. Unresolved
|
|
573
|
+
references, sensitive dynamic-property schemas or other unsupported applicators,
|
|
574
|
+
and traversal limits withhold the entire echo. A private array element withholds
|
|
575
|
+
its containing array to preserve indices. `argsRedacted: true` marks a partial
|
|
576
|
+
or withheld echo as reconciliation context; retries require the original arguments.
|
|
491
577
|
OAuth discovery and consent URLs are refused as a typed, non-retryable failure
|
|
492
578
|
when they contain a known credential, before persistence or reuse. This guard
|
|
493
579
|
checks the request's sent credentials and the current grant's credentials,
|
|
@@ -604,6 +690,27 @@ switched by the environment, and keep their entries under 30 lines.
|
|
|
604
690
|
|
|
605
691
|
### Providers and reviewed classification
|
|
606
692
|
|
|
693
|
+
Infisical separates explicit value reads from identifier results. `get_secret`
|
|
694
|
+
and `list_secrets` with `includeValues: true` return secret values and comments.
|
|
695
|
+
All other results, including imported secrets and pending approvals, allow only
|
|
696
|
+
checked IDs, keys, environment and tag slugs, paths, timestamps, positive versions
|
|
697
|
+
and enum fields. They omit project slugs derived from free-text names, comments,
|
|
698
|
+
descriptions, free-form names, reminder notes and arbitrary nested objects.
|
|
699
|
+
`metadataOmitted: true` reports withheld or unavailable metadata.
|
|
700
|
+
This assumes an honest server and protects against careless human entry in free
|
|
701
|
+
text. A malicious server encoding values into valid identifiers is out of scope.
|
|
702
|
+
Known, accepted residual risk: environment slugs are returned as required
|
|
703
|
+
routing identifiers. Infisical's UI derives them from environment names by
|
|
704
|
+
default, so secrets entered in those names can appear in returned slugs.
|
|
705
|
+
Operators must not put secrets in environment names or slugs. Secrets placed in
|
|
706
|
+
identifiers or key names are out of scope.
|
|
707
|
+
Write input schemas mark secret values, secret comments and folder descriptions
|
|
708
|
+
as `writeOnly`, so recovery argument echoes omit them without changing dispatch.
|
|
709
|
+
Submitted values never join the request's sent-credential set, preserving later
|
|
710
|
+
program arguments. Client credentials and tokens still join that set on every
|
|
711
|
+
call. The [provider guide](https://github.com/zackbart/connecta/blob/main/src/providers/infisical/SKILL.md) owns routing and
|
|
712
|
+
value-read conventions.
|
|
713
|
+
|
|
607
714
|
A maintained provider is one `defineProvider()` call (`src/provider.ts`): a
|
|
608
715
|
name, title, kind (`"mcp"`, `"api"`, or `"composed"`), a maintained skill, an
|
|
609
716
|
optional reviewed classification, a closed `options` shape declared with
|
|
@@ -1010,82 +1117,7 @@ compiling and configuring the real thing.
|
|
|
1010
1117
|
|
|
1011
1118
|
## Operator data contract
|
|
1012
1119
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
visible connectors, granted tools, classification overrides, and admitted
|
|
1018
|
-
pools. Pool tools are the intersection with the caller's identity grants.
|
|
1019
|
-
`configSources` labels each serialized leaf as `default` or `config`, based on
|
|
1020
|
-
option presence rather than equality with the default. Factory descriptions carry
|
|
1021
|
-
relative `optionSources` paths so resolved module and transport defaults retain
|
|
1022
|
-
their provenance. The snapshot copies only resolved values; the UI consumes
|
|
1023
|
-
these presence facts separately. Its paths use connector
|
|
1024
|
-
ids and pool names and are filtered after disclosure; hidden values leave no
|
|
1025
|
-
provenance keys. It contains no source code or credential material.
|
|
1026
|
-
Every response is private, `no-store` JSON. Only GET is allowed, with the
|
|
1027
|
-
same auth gate and identity partition as `/ui/data` and `/ui/connectors/:id`.
|
|
1028
|
-
|
|
1029
|
-
`live.connectors` carries status and problem codes, complete registry tools
|
|
1030
|
-
with their final read/write classification, catalog age in milliseconds, and
|
|
1031
|
-
last-call time/outcome. Catalog descriptions and schemas are allowed for an
|
|
1032
|
-
authenticated reader; grammar-failing names and addresses become `<withheld>`.
|
|
1033
|
-
`auth.registrationPath` reports the selected downstream OAuth client mechanism when known.
|
|
1034
|
-
Status prose, error text, credential values or suffixes, arguments, results,
|
|
1035
|
-
and code are excluded. Static and unobserved catalogs have a null age;
|
|
1036
|
-
persisted catalogs retain their original fetch time. Probes run with bounded
|
|
1037
|
-
concurrency and the configured probe deadline, and release their scopes.
|
|
1038
|
-
|
|
1039
|
-
Last-call lookup requires the existing activity permission and read gate. It
|
|
1040
|
-
scans up to 1,000 recent rows, filters connector/tool grants, and restricts
|
|
1041
|
-
personal connectors to the caller's activity identity. Each retained fact
|
|
1042
|
-
copies a validated timestamp and outcome only. A null call means unknown or
|
|
1043
|
-
absent in that window, not proof of no calls. `live.activity` distinguishes
|
|
1044
|
-
available, unconfigured, forbidden, and unavailable history. `you` names no
|
|
1045
|
-
identity or auth material; it reports grants, root and admitted-pool trust,
|
|
1046
|
-
and effective activity, token, and per-connector auth permissions.
|
|
1047
|
-
|
|
1048
|
-
`connecta doctor --config` fetches this authenticated contract and prints only
|
|
1049
|
-
`config` as indented JSON. It requires the UI module and the existing doctor
|
|
1050
|
-
authentication environment variables; it runs no diagnostic program, follows
|
|
1051
|
-
no redirects, and prints no raw HTTP failure body.
|
|
1052
|
-
|
|
1053
|
-
The operator shell uses React, TanStack Router/Query/Table, Radix, cmdk and
|
|
1054
|
-
Tailwind v4. Inter, CSS, JavaScript and dependency notices are hashed assets
|
|
1055
|
-
under `/ui/assets/*`, served identically with immutable cache headers on Node
|
|
1056
|
-
and Workers. HTML contains only mount points and escaped inert configuration;
|
|
1057
|
-
it remains uncached. `generated.ts` is ignored and generated before build and
|
|
1058
|
-
test; `check:operator-ui` detects stale assets and shared page styles. The UI
|
|
1059
|
-
stays behind `./ui`, outside the root import graph. The identity-fenced store
|
|
1060
|
-
owns authenticated reads and existing mutations. Overview, Connectors, connector
|
|
1061
|
-
detail, Tools, Access and Config consume the typed contract. Activity retains
|
|
1062
|
-
its authenticated data routes. Connector detail tabs live
|
|
1063
|
-
in the URL hash; Activity filters live in the query string and apply to loaded
|
|
1064
|
-
history. Both `/ui/activity` and `/ui/api/activity` require Activity access and
|
|
1065
|
-
pass the optional activity `readGate`. Interactive operators and machine callers
|
|
1066
|
-
admitted by `identity.activityAccess` share the same history disclosure rules.
|
|
1067
|
-
Connector/tool grants and grants for the recorded pool filter each event. Shared
|
|
1068
|
-
connector history can include other principals' events, with recorded request
|
|
1069
|
-
IDs, validated client name/version and package version, and actor kind, ID and
|
|
1070
|
-
namespace. Interactive reads may also add directory actor labels; machine reads
|
|
1071
|
-
never add them. Personal connector history is owner-only: the event must have a
|
|
1072
|
-
checked `principal` actor basis and actor ID/namespace matching the reader's
|
|
1073
|
-
admitted principal. Rows without a provable personal owner are withheld. Activity
|
|
1074
|
-
carries no arguments, results, code or raw downstream errors. The last-call
|
|
1075
|
-
overlay uses the same event filter but returns only timestamp and outcome.
|
|
1076
|
-
Calls group only by recorded request ids, with missing ids shown as
|
|
1077
|
-
separate calls. Classification and result size are shown only when recorded. The verdict is
|
|
1078
|
-
captured after registry resolution, and result size counts UTF-8 bytes of the
|
|
1079
|
-
downstream value before paging or truncation. Nullable SQL columns preserve old
|
|
1080
|
-
rows. Catalog changes use the same paging envelope with `kind: "catalog_drift"`,
|
|
1081
|
-
`source: "catalog_refresh"` and a checked `catalog_changed` fact containing only
|
|
1082
|
-
added, removed and changed tool counts. The first complete catalog is a baseline;
|
|
1083
|
-
one refresh-publication hook emits each later change. It includes the request's
|
|
1084
|
-
id and typed actor when available, otherwise a fresh id and system actor. No
|
|
1085
|
-
names, descriptions or schemas enter the event. Private catalog changes require
|
|
1086
|
-
a matching principal owner even when the connector uses shared auth. Empty credential declarations
|
|
1087
|
-
report `credential_required` before running a connector status or catalog probe.
|
|
1088
|
-
The Access adapter reports inbound provider kinds, admitted pools, grants, trust
|
|
1089
|
-
and endpoint setup; token controls retain their existing permission gate.
|
|
1090
|
-
Configuration remains in deployment code. The UI mutates only credentials,
|
|
1091
|
-
OAuth connections and client tokens through the existing routes.
|
|
1120
|
+
The contract now lives in [Operator UI](./operator-ui.md#operator-data-contract),
|
|
1121
|
+
including configuration provenance, live facts, activity disclosure, doctor,
|
|
1122
|
+
and the browser implementation. [Optional deployment modules](#optional-deployment-modules)
|
|
1123
|
+
keeps the module composition and request-lifetime rules here.
|
package/documentation/auth.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Inbound auth
|
|
2
2
|
|
|
3
|
+
## Task routes
|
|
4
|
+
|
|
5
|
+
[Agent index](./README.md) distinguishes endpoint operation from deployment
|
|
6
|
+
and package maintenance. This guide owns identity, grants, and downstream
|
|
7
|
+
credential contracts; use [deployment setup](./deploying.md) for runnable steps.
|
|
8
|
+
|
|
9
|
+
| Task | Sections |
|
|
10
|
+
| ----------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| Connect a machine or migrate an old client | [Managed tokens](#managed-client-tokens-and-upgrading-from-v023), [static bearer migration](#migrating-static-bearer-clients) |
|
|
12
|
+
| Configure caller visibility, trust, or auth ownership | [Principals and operators](#principals-visibility-and-operators), [pools](#pools), [shared and personal auth](#shared-and-personal-auth) |
|
|
13
|
+
| Set up human sign-in | [Access on Workers](#cloudflare-access-on-workers), [Clerk tokens and sessions](#clerk-oauth-tokens-and-operator-sessions), [management](#human-authentication-management) |
|
|
14
|
+
| Enable credential or connection controls | [Management permissions](#management-permissions), [start/restart/disconnect](#starting-restarting-and-disconnecting) |
|
|
15
|
+
| 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) |
|
|
16
|
+
| 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) |
|
|
17
|
+
| Review browser or downstream URL boundaries | [Origins](#origins), [advertised URLs](#urls-a-downstream-advertises), [one server per grant](#one-server-per-grant) |
|
|
18
|
+
|
|
19
|
+
[Credential recognition and admission](#credential-recognition-and-admission)
|
|
20
|
+
defines refusal precedence.
|
|
21
|
+
[Source and tests](#source-and-tests) points to the implementation. Migration
|
|
22
|
+
sections describe upgrade procedures, not additional auth modes to enable.
|
|
23
|
+
|
|
24
|
+
## Credential recognition and admission
|
|
25
|
+
|
|
3
26
|
Inbound auth decides who may reach the MCP endpoint. People use `clerkAuth`
|
|
4
27
|
from `@zackbart/connecta/auth/clerk` or `cloudflareAccessAuth` from
|
|
5
28
|
`/auth/cloudflare-access`. Machines use `accessTokens(storage)` from
|
|
@@ -369,7 +392,14 @@ longer live; one left behind cannot complete and expires with its link.
|
|
|
369
392
|
| `DELETE /ui/oauth/<id>` | Disconnects and invalidates the cached catalog, even if the browser leaves. |
|
|
370
393
|
|
|
371
394
|
`authorize_connector` issues the same browser link, with `force` carried in the
|
|
372
|
-
signed handoff.
|
|
395
|
+
signed handoff. On hosts without URL elicitation, direct and program auth
|
|
396
|
+
failures and search/describe catalog failures include the same authorization
|
|
397
|
+
handoff. The admitted registry and management permission govern issuance.
|
|
398
|
+
Each call or program run shares at most one handoff per connector; errors never
|
|
399
|
+
request a forced restart. Signed links retain their fifteen-minute expiry,
|
|
400
|
+
initiating-user and owner binding, origin check, and single-use browser nonce.
|
|
401
|
+
Minting a link stores no nonce and starts no consent. When an auth error includes
|
|
402
|
+
a handoff, give it to the user or operator. Status reads never call `startAuth` and never expose the
|
|
373
403
|
provider's consent URL. A UI start answers `{ state: "auth_required",
|
|
374
404
|
authorizationUrl }`, where the URL belongs to connecta. Continue hands back the
|
|
375
405
|
latest consent only while it is unclaimed, in the live epoch, written within
|
|
@@ -574,7 +604,7 @@ withheld error keeps its original classification, and no connector error keeps
|
|
|
574
604
|
a runtime, stream, or parser error as `cause`.
|
|
575
605
|
|
|
576
606
|
The JSON-RPC and 4xx exception preserves diagnostics agents need to correct
|
|
577
|
-
their arguments. Core redacts credentials
|
|
607
|
+
their arguments. Core redacts credentials and submitted `writeOnly` argument values before these
|
|
578
608
|
messages, nested causes/data, or tool results reach an agent or guest program.
|
|
579
609
|
The memory-only set covers credential-slot values, static auth headers,
|
|
580
610
|
outbound bearer tokens, and their JSON-escaped, URL-encoded, base64 and
|
|
@@ -586,6 +616,85 @@ maintained-provider transports track the final request too. Credential-slot
|
|
|
586
616
|
reads cover values sent in headers, queries, or bodies. Echoed sensitive
|
|
587
617
|
header lines are also withheld.
|
|
588
618
|
|
|
619
|
+
Submitted values under JSON Schema `writeOnly: true` join this memory-only
|
|
620
|
+
request set, including nested objects, arrays, and local references. Strings
|
|
621
|
+
and numbers register with the same raw, URL, form, base64, and base64url forms
|
|
622
|
+
as credentials; booleans and null carry no text. Property names inside a
|
|
623
|
+
private object subtree are private values too; array indices are not. Output
|
|
624
|
+
redaction protects the union of private fields across `oneOf`/`anyOf`
|
|
625
|
+
alternatives. If a schema declares private fields but its sensitivity cannot
|
|
626
|
+
be resolved, all submitted values are treated as private. Schemas without a
|
|
627
|
+
`writeOnly` annotation skip this work. Private values never enter stored
|
|
628
|
+
catalogs or operator records. The program's outgoing-argument filter and
|
|
629
|
+
catalog caches use credentials alone, so later calls receive the original
|
|
630
|
+
submitted data and cached catalogs never depend on one request's arguments.
|
|
631
|
+
|
|
632
|
+
Payload rules apply where downstream or guest data enters: downstream results
|
|
633
|
+
and errors, resource reads, connector usage guides and downstream skill files,
|
|
634
|
+
program returns, logs, and emits, and relayed input prompts. JSON objects,
|
|
635
|
+
arrays, and string literals are parsed and walked, whether they are wire text
|
|
636
|
+
or a string inside a result, so nested JSON strings are decoded too. Every
|
|
637
|
+
decoded string leaf and property name is scanned, and a number, boolean, or
|
|
638
|
+
null whose canonical text matches becomes the string `[redacted]`, so the JSON
|
|
639
|
+
stays valid. Text is reserialized
|
|
640
|
+
only when something changed; otherwise its original bytes remain. The
|
|
641
|
+
serialized bytes are then scanned raw, and a literal the walk cannot see, such
|
|
642
|
+
as one spanning tokens, withholds the whole text. Other text is scanned raw and
|
|
643
|
+
through one more layer of JSON string escapes at a time while the last layer
|
|
644
|
+
could still hold an escape. A string is decoded through at most eight layers in
|
|
645
|
+
total, counting the nested JSON strings it was parsed from; text that would
|
|
646
|
+
need more is withheld. Every match is mapped back to the original text,
|
|
647
|
+
overlapping and adjacent spans merge, and each merged span is replaced once, so
|
|
648
|
+
one match can never split another. A span wholly inside an existing
|
|
649
|
+
`[redacted]` placeholder is that placeholder; a private value that contains the
|
|
650
|
+
placeholder text is still redacted. Connecta and MCP envelope field names, tag
|
|
651
|
+
values such as a content `type` or result `format`, and typed facts such as
|
|
652
|
+
`isError` and `retryable` are copied unchanged. The agent boundary around
|
|
653
|
+
Connecta's own envelopes repeats credential and literal private-value
|
|
654
|
+
replacement without the short-value rules, so envelope identifiers, counts, and
|
|
655
|
+
JSON-RPC ids are never rewritten.
|
|
656
|
+
|
|
657
|
+
Empty private strings are exempt because they contain nothing to leak. A
|
|
658
|
+
non-empty private value shorter than eight characters redacts a structured
|
|
659
|
+
string leaf, property name, or scalar only when it equals the value or a
|
|
660
|
+
supported encoded form, including after JSON unescaping. A longer structured
|
|
661
|
+
field that merely contains it, such as `prod-db` for `prod`, is kept: this
|
|
662
|
+
documented tradeoff keeps identifiers and approval state usable. Prose that
|
|
663
|
+
contains a short value or encoded form anywhere is withheld as `[redacted]`:
|
|
664
|
+
plain-text results, `message`, `error`, `text`, `logs`, and `stack` strings,
|
|
665
|
+
string program results, and relayed prompts. Typed error classification,
|
|
666
|
+
retryability, retry delay, and write-outcome accounting remain. Private values
|
|
667
|
+
of at least eight characters are replaced wherever they occur.
|
|
668
|
+
|
|
669
|
+
Matching runs one Aho-Corasick automaton over every registered form, so each
|
|
670
|
+
scanned code unit costs amortized constant work however many values are
|
|
671
|
+
registered. The automaton indexes the first 256 code units of each form,
|
|
672
|
+
credentials first, and at most 512 Ki indexed units for every registration
|
|
673
|
+
together, so a long credential or private value costs no more automaton memory
|
|
674
|
+
than its prefix. Where a longer form's prefix matches, the scanned view's
|
|
675
|
+
rolling hash confirms the rest once its last unit arrives; equal text always
|
|
676
|
+
confirms, so a hash collision could only redact more. Forms that share a prefix
|
|
677
|
+
and a length share each confirmation, and overlapping prefix occurrences in
|
|
678
|
+
self-similar text share one run of confirmations, so periodic text stays exact.
|
|
679
|
+
Each confirmation is charged to the call's work budget before it is queued, and
|
|
680
|
+
one scanned view holds at most 16 Ki runs; a view that needs more is withheld.
|
|
681
|
+
Submitted values register at most 512 KiB of encoded forms and 4,096
|
|
682
|
+
argument nodes. A form that does not fit either limit withholds every string
|
|
683
|
+
at least as long as that form; an argument tree that cannot be walked withholds
|
|
684
|
+
all downstream text.
|
|
685
|
+
|
|
686
|
+
One redaction call has one work budget across parsing and every scanned view:
|
|
687
|
+
64 Mi units with private values, and 1 Gi for credentials alone, which keeps
|
|
688
|
+
multi-MiB document and skill-file reads. Work that does not fit the remaining
|
|
689
|
+
budget withholds its field, so smaller identifiers can still be checked. The
|
|
690
|
+
same call walks structured values iteratively, at most 512 containers deep and
|
|
691
|
+
1 Mi entries in total, and copies only containers on a path that changed. A
|
|
692
|
+
container's entries are counted before it is walked. One that does not fit the
|
|
693
|
+
remaining entries is scanned as serialized text instead: kept unchanged when
|
|
694
|
+
nothing registered appears, otherwise withheld. A cycle or a container past the
|
|
695
|
+
depth limit is withheld. No limit throws or exposes a matcher diagnostic, and
|
|
696
|
+
typed success outcomes remain.
|
|
697
|
+
|
|
589
698
|
Only credentials of at least eight characters enter the redaction and structural
|
|
590
699
|
matchers. Credentials shorter than 8 characters are not redacted from echoes;
|
|
591
700
|
use longer secrets. Matching short secrets can corrupt JSON-RPC fields or refuse
|
|
@@ -594,7 +703,7 @@ this floor produces one operator warning per connector with the typed code
|
|
|
594
703
|
`short_secret_not_redacted` and no credential value. Remote connectors warn at
|
|
595
704
|
construction; static `api()` OAuth warns on its first provider operation.
|
|
596
705
|
Connecta's own messages never quote credential values, regardless of length.
|
|
597
|
-
One
|
|
706
|
+
One automaton is cached until the secret set changes, and an empty set skips matching. Redaction runs after
|
|
598
707
|
JSON unwrapping or joining text blocks and on final serialized text and every
|
|
599
708
|
structured string, before result paging, emits, and program outputs/errors/logs. See
|
|
600
709
|
[the agent boundary](./architecture.md#errors-and-records).
|
|
@@ -962,8 +1071,14 @@ still serve configured credentials. Both routes accept only GET, otherwise retur
|
|
|
962
1071
|
`form_post` is unsupported. Clerk's session-only policy applies regardless of method.
|
|
963
1072
|
|
|
964
1073
|
The fifteen-minute signed handoff carries connector ownership, initiating principal, deployment origin, nonce,
|
|
965
|
-
|
|
966
|
-
|
|
1074
|
+
restart mode, and any predecessor link binding. New links use versioned, length-prefixed fields instead of
|
|
1075
|
+
JSON, with unpadded base64url payload and signature segments. Ordinary handoffs use an opaque wire encoding,
|
|
1076
|
+
not encryption; URL elicitation also seals those fields as described above. Copy the complete URL unchanged.
|
|
1077
|
+
The signature authenticates the exact version and payload text. Tampered or re-encoded links return
|
|
1078
|
+
`Invalid or expired connection link. Request a new link from connecta.` Pre-upgrade signed JSON and sealed
|
|
1079
|
+
v2 links still verify against their original text and expire after their original fifteen minutes.
|
|
1080
|
+
|
|
1081
|
+
Configure `vault: encryptedCredentialVault(storage, key)`, which derives its HMAC key with HKDF. Custom vaults need `signOAuthHandoff` and `verifyOAuthHandoff`; without a signing vault, links cannot be
|
|
967
1082
|
issued or accepted. A link grants no identity. After identity and permissions pass, its nonce is consumed
|
|
968
1083
|
before OAuth starts, even while consent is pending. Failed starts need new links; nonce markers expire with
|
|
969
1084
|
their links.
|
|
@@ -1145,3 +1260,21 @@ is retained across requests, and rejected writes are never replayed. An omitted
|
|
|
1145
1260
|
`app.privateKey` uses the encrypted connector vault's `privateKey` field;
|
|
1146
1261
|
`describe()` includes neither the key nor the minted tokens. The maintained
|
|
1147
1262
|
provider skill explains App permissions and installation prerequisites.
|
|
1263
|
+
|
|
1264
|
+
## Source and tests
|
|
1265
|
+
|
|
1266
|
+
Inbound adapters live in [src/auth](https://github.com/zackbart/connecta/tree/main/src/auth);
|
|
1267
|
+
[auth routes](https://github.com/zackbart/connecta/blob/main/src/routes/oauth.ts)
|
|
1268
|
+
own connection handoffs and callbacks. Check [inbound auth tests](https://github.com/zackbart/connecta/blob/main/test/inbound-auth.test.ts),
|
|
1269
|
+
[Access tests](https://github.com/zackbart/connecta/blob/main/test/cloudflare-access-auth.test.ts),
|
|
1270
|
+
and [identity-scope tests](https://github.com/zackbart/connecta/blob/main/test/identity-scope.test.ts)
|
|
1271
|
+
for credential precedence and disclosure boundaries.
|
|
1272
|
+
|
|
1273
|
+
Downstream OAuth implementation and evidence are in
|
|
1274
|
+
[downstream-oauth.ts](https://github.com/zackbart/connecta/blob/main/src/auth/downstream-oauth.ts),
|
|
1275
|
+
[static-oauth.ts](https://github.com/zackbart/connecta/blob/main/src/auth/static-oauth.ts),
|
|
1276
|
+
[OAuth connect tests](https://github.com/zackbart/connecta/blob/main/test/oauth-connect.test.ts),
|
|
1277
|
+
[callback fencing](https://github.com/zackbart/connecta/blob/main/test/oauth-callback-fencing.test.ts),
|
|
1278
|
+
[refresh leases](https://github.com/zackbart/connecta/blob/main/test/oauth-refresh-lease.test.ts),
|
|
1279
|
+
and [API OAuth tests](https://github.com/zackbart/connecta/blob/main/test/api-oauth.test.ts).
|
|
1280
|
+
Use the test pointers within each section for narrower failure cases.
|