Repository navigation
Build full-surface Effect Bridge SDK for Miso #59
Description
Build @misofm/bridge in packages/bridge as a portable, npm-ready Effect v4 SDK that Miso can use from its private Cloudflare Bridge Worker. Do not publish or use the live key during this work.
Draft implementation PR: #60. Keep this issue open for the remaining release gate below.
Source and scope
- Bridge API reference, documentation index, and official OpenAPI specification.
- Snapshot retrieved 2026-09-23; SHA-256
e8aee6cee1b5518d81f384c8b718dd9d9154dd9eb55345ff73d8b8dbe88327f2. OpenAPI 3.0.2, 100 paths, 134 operations, 263 schemas. Cover all 134, including fourx-hiddenDeveloper Fees operations and deprecated legacy endpoints; label those clearly. - Follow the Effect skill. Match this repository's
effect@4.0.0-rc.112exactly. Use@effect/openapi-generator@4.0.0-rc.112withformat: "httpapi"for Effect Schema contracts andHttpApiClientencoding/decoding. Keep a raw pinned spec, deterministic operation-name manifest, and reviewable normalization overlay. Never hand-edit generated files.
Coverage: customer onboarding/compliance (Customers 15, Associated Persons 3, KYC Links 3, RFIs 2, Sandbox 1); bank accounts (External Accounts 9, Fiat Payout Configuration 2, Plaid 2); wallets/prefunding (Bridge Wallets 7, Prefunded Accounts 3); money movement (Transfers 8, Travel Rule 1, Crypto Return Policies 4); collection infrastructure (Liquidation Addresses 10, Virtual Accounts 9, Static Memos 7, Batch Settlements 1); Cards 21; Webhooks 8; developer administration (Developers 4, API Keys 2, Developer Fees 4); supporting information (Exchange Rates 1, Lists 2, Funds Requests 1, Rewards 4).
Public contract and safety requirements
Expose the generated BridgeApi, schemas, grouped operations, a Bridge Context service, client factory, injectable Layer, and Fetch-backed Layer. Consumer owns the Effect runtime. Use a redacted API key in the Api-Key header; keep credentials, KYC data, account details, and full requests out of printable errors/logs. Preserve snake_case wire fields and decimal strings. Support production, sandbox, and custom URLs without leaking credentials across redirects. Distinguish schema, HTTP, transport, timeout, and cancellation outcomes. No implicit mutation retries; caller-provided idempotency keys must remain stable. Support binary PDF responses and no-content endpoints. Provide incremental pagination Streams and portable webhook verification/event decoding. Include a compile-checked Cloudflare Worker example reading the key from the current invocation's binding.
Document and test upstream discrepancies instead of weakening all schemas: POST /api_keys explicitly forbids Idempotency-Key despite the general POST guide; four other POSTs omit that header declaration (external-account verification, Plaid token exchange, and two PDF statement routes). Customer examples disagree with the schema on type, nullable business last_name, and list count. WebhookEvent.event_type examples include qualified values absent from the enum. POST /developer/fees lacks a success schema. Rewards history uses a distinct cursor with limit 90; pending card authorizations are unpaginated. Four card POSTs have optional/unspecified request bodies in the raw spec but are generated as required; resolve or document this precisely. Bridge's webhook guide requires SHA-256 of timestamp.rawBody, then RSA PKCS#1 v1.5 SHA-256 verification of that digest; verify with independent vectors and note the inconsistent Ruby example.
Bounded agent tasks
Give each task one fresh GPT-6 Luna max agent, with disjoint file ownership. Coordinate contract changes through this issue before editing another task's files.
- T1 — Package foundation and generated contract. Own
packages/bridge/package.json,tsconfig*.json,LICENSE,AGENTS.md,openapi/**,scripts/generate.ts,src/generated/**,tests/generation.test.ts. Snapshot provenance; assign stable operation IDs/groups; generate all 134 operations and concrete Effect Schemas offline; review all generator warnings; byte-for-bytegenerate:check; report actual client method shape. No runtime generator/spec dependency. Top-levelSchema.Neverand webhook failures repaired with directional request/response bank schemas; byte-for-byte generation and package checks pass. - T1b — Nested generated-schema repair. One fresh Luna max agent after T3, limited to the generator overlay and targeted tests. Resolved remaining
Schema.Neveroccurrences inCustomerendorsement maps,VirtualAccountResponse.source_deposit_instructions,CardProgramSummarycountry-stat maps, and virtual-account deactivation response. Raw snapshot/manifest unchanged; focused non-empty fixtures, generation check, typecheck and package suite pass (58 tests, 1,488 assertions; zero generatedSchema.Never). - T2 — Authenticated Effect client. After T1, own
src/Bridge.ts,Config.ts,Errors.ts,internal/http.ts,tests/client.test.ts,tests/client-types.ts. Implement service/factory/Layers, credentials/config, cancellation and typed failures, idempotency and response metadata. Test redirect isolation, malformed/invalid responses, non-JSON errors, timeouts, PDF/bodyless responses, and API-key exception. Completed, independently audited, and hardened through T2b–T2i; the remaining type-erasedundefinedcase is listed under the release gate. - T3 — Pagination. After T1/T2 contract freeze, own
src/Pagination.ts,tests/pagination.test.ts. Stream standard cursor lists and Rewards cursor history; preserve filters/order, reject incompatible cursors and cycles, terminate only on empty page/valid completion, support early cancellation. Do not invent pagination for unpaginated endpoints. Independently reviewed with 17 focused tests passing. - T4 — Webhooks. May start alongside T1; own
src/Webhooks.ts,tests/webhooks.test.ts,tests/fixtures/webhooks/**. Verify raw bytes with Web Crypto and Effect Clock before decoding; reject tampering, bad key/header/base64, stale/future timestamps; independently generated signature vectors; allow deliberate handling of unknown event categories. No Node runtime imports. - T5 — Full-surface evidence. After T1/T2, own
tests/coverage.test.ts,contracts.test.ts,contract-types.ts,fixtures/contracts/**,support/**,docs/UPSTREAM.md. Independently compared raw spec, manifest, and public client for all 134 verb/path/param/body/status/media contracts; controlled fixtures cover every major family and Miso's KYC → wallet/account → transfer → webhook path. Its optional-card body finding was repaired by T2b/T1c/T2e/T2f. - T2b — Optional-card request-body wire repair. Bounded follow-up to T2 client/runtime, preserving generated call types. On the four optional card POSTs,
payload: undefinednow sends no body/no JSONContent-Type; supplied valid JSON remains intact. Explicit null and other invalid supplied bodies are rejected by T2e/T2f. - T1c — Optional-card generator union order. Direct rc.112 encoding showed
[NoContent, RequestJson]erased supplied JSON objects. A guarded deterministic generator correction reverses only the four optional-body payload unions; raw spec and manifest unchanged. Direct absent/present encode tests and offline generation check pass. - T6 — Package integration and consumers. After T1–T5, own
src/index.ts, README, examples, isolated consumer fixture/script, root build/CI/publish workflow/docs and lockfile. ESM/declarations, packed Node ESM consumer and all subpaths, Cloudflare compilation/local workerd mock, and root build/typecheck pass.bridge-v*validates but skips npm publication; no deploy or secret access. - T7 — Independent verification for draft review. GPT-6 Astra xhigh read-only audits independently checked full coverage, security, runtime, consumers, and each bounded fix. Final spot-review returned go for a draft PR after the callable-request guard. All recorded gates pass. This is not npm release or live Bridge wire-compatibility sign-off; see remaining release gate below.
Astra xhigh findings and bounded repairs
- P1 / T4b — Webhook byte snapshot.
Buffer.slice()aliased caller memory; intrinsic copies now isolate verification and decoding. Buffer/subarray mutation regressions pass with real RSA; full Bridge suite passes (66 tests). - P1 / T2c — Sensitive error containment. Non-2xx now fails with safe status/method/request-ID metadata before decoding upstream error JSON; all deferred body readers and stream failures use a safe response facade. Printable inspection redacts configured headers. Package typecheck, focused client tests (20), and full Bridge suite (71) pass.
- P1 / T1d — Object-schema normalization. Explicitly inventoried 114 remaining non-composed property-bearing schema nodes with an exact generator guard, normalized them to open objects, and added positive/negative top-level and nested fixtures. Generation and 75 package tests pass; test-only type errors and an explicit-null optional-card runtime seam are separate follow-ups.
- T2e — Explicit-null optional card preflight. The typed card request objects reject
null, and a four-method client preflight now rejects explicitpayload: nullbefore network I/O. Undefined remains bodyless, valid objects remain JSON; 20 focused client and 75 package tests pass. Only T5 test-type adjustments remain. - P2 / T2d — KYC-safe tracing. Bridge calls now suppress only their own HTTP child span and trace propagation before private URL attributes are recorded. Captured-span regression confirms outer and subsequent non-Bridge tracing still work; package typecheck and 77 Bridge tests pass. README must document lost Bridge per-request span metadata.
- P3 / T5b — Documentation and evidence. Fixed stale
UPSTREAM.mdstatements and test-only type errors; added a deterministic pinned-example audit. Full Bridge suite and typecheck pass (76 tests, 2,160 assertions). Published business-customer and customer-list response examples remain strict-decoding risks; output-only compatibility is a separate T1e task below. - T1e — Customer response-example compatibility. Output-only overlay now accepts nullable
Customer.last_name, structuredendorsements[].requirements.completeentries, and omittedGetCustomers200.countin Bridge's published successful examples. Create/update inputs remain strict; raw snapshot/manifest pinned. Generation/typecheck/build pass, and T5c updated the evidence. Transfer currency remains unmodified pending Bridge confirmation. - T5c — Customer example evidence refresh. Business/list official examples now assert acceptance; unresolved transfer/verification/virtual-account negatives remain.
UPSTREAM.mdreflects implemented output overlay and live-wire uncertainty; typecheck and 78 package tests pass. - P2 / T6c — README accuracy and durable workerd CI check. Public docs now reflect safe HTTP errors, deferred response facade, suppressed Bridge HTTP tracing, and output-only customer compatibility. Pinned Wrangler 4.137.0 local-workerd smoke runs in CI with synthetic binding/mock upstream and process cleanup. Frozen install, workspace typecheck/tests/build, packed consumer, and local-workerd smoke pass; no deploy or live API.
- P1/P2 / T2f — Runtime request validation escape. Fresh Astra re-review found rc.112
NoContentunion fallback dropped malformed optional-card payloads into JSONnull, andFormDatabypassed JSON schema encoding. A bounded transport guard validates supplied card bodies against their concrete generated schemas and rejects FormData for generated JSON-only methods before I/O. Final independent review covered these paths. - P2 / T2g — Lazy request mutation snapshot. Astra found the T2f guard captured
request.payloadwhen an Effect was constructed, while the generated client read it later during execution. The wrapper now guards at execution time, snapshots request fields, and passes validated/decoded JSON into the generated operation. Regression cases cover valid-to-invalid and JSON-to-FormData mutations, absence-to-valid card payload, and mutation during decode; final independent review passed. - P2 / T2h — Getter/decoder defect containment. Astra found that caller-controlled throwing getters at snapshot/decode escaped as printable Effect defects. A bounded wrapper repair turns snapshot/clone exceptions and decoder defects into redacted request-phase
BridgeSchemaErrorwithout altering interruption or unrelated failures. Getter/proxy/Cause redaction regressions and final independent review pass. - T2i — Callable/null top-level request guard. Final Astra probe found callable requests bypassed the object snapshot and could expose a thrown getter. The guard now rejects non-undefined non-object and null request arguments before invocation; callable/proxy/null/primitive probes return redacted
BridgeSchemaErrorwithout I/O. Agent thread cap prevented a fresh Luna for this final small fix; the main agent patched it and the Astra xhigh reviewer independently confirmed it. All 84 Bridge tests and full gates pass.
Remaining release gate
- Before npm publication or production integration, resolve the type-erased
undefinedrequest case on methods requiring path parameters: afreeze(undefined)call currently sends an authenticated empty POST with unresolved path placeholders. The final Astra audit judged this non-blocking for a draft PR but not something to claim as full pre-I/O validation. The README documents it. Also reconcile the documented Bridge schema/example conflicts, especially transfer responsecurrency, against approved sandbox/live traffic once KYC is complete.
Completion gates: offline deterministic generation; package and workspace typecheck/test/build; 134/134 contract coverage; packed tarball import/typecheck/runtime check; Cloudflare consumer smoke check; independent Astra findings resolved. Record limitations of mocked tests and any unresolved upstream ambiguity. KYC onboarding and live Bridge access are not required for these gates.
Activity