You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hyperdrive bindings are currently local-only in local dev: Miniflare connects directly to the
origin database via localConnectionString. For databases that are only reachable through a
private network — e.g. Hyperdrive configs whose origin is a tcp-type VPC Service (#12874) — there
is nothing a developer can put in localConnectionString, so local dev is simply not possible.
Now that raw TCP connect() tunnelling over remote bindings has landed for VPC Networks/Services
(#14712), the same relay can carry Hyperdrive traffic. @xortive suggested exactly this in #14712 (comment). We prototyped
it end-to-end against the real edge and are proposing a design here before sending a PR
(per CONTRIBUTING's discuss-first guidance), because one part of the semantics deserves
maintainer input.
What we verified empirically (all against a real preview session + real private MySQL)
Preview sessions already provision Hyperdrive raw bindings. The remote-bindings pipeline is
type-agnostic; startRemoteProxySession({ HYPERDRIVE: { type: "hyperdrive", id } }) works today.
The edge side needs no changes.env.HYPERDRIVE.connect() in the ProxyServerWorker context
returns a socket to Hyperdrive's MySQL front-end; the address argument is ignored (same
config-decides-routing model as VPC Services), so the existing MF-Connect-Address tunnel
endpoint from [miniflare] Support connect() on remote VPC Network bindings in local dev #14712 works unmodified.
connectionString is fully synthetic on both ends: magic <hex>.hyperdrive.local host, per-session generated dummy credentials, and the config id in the database slot. The client
authenticates to Hyperdrive with these; Hyperdrive holds the real origin credentials.
Full path proven: local TCP bridge → WebSocket relay (the [miniflare] Support connect() on remote VPC Network bindings in local dev #14712 tunnel, unmodified) →
edge Hyperdrive → real origin. A stock mysql2 pool authenticated successfully and SELECT VERSION() returned the origin server's version (8.0.25) — i.e. post-auth queries
are proxied to the real database, exactly like production.
Two constraints that shape the design
(a) The Hyperdrive designator must stay external (tcp). Our first prototype pointed the
Hyperdrive binding's designator at the shared remote-proxy-client Worker (the D1/VPC pattern) —
workerd segfaults in that configuration (crash in the Hyperdrive JSG connect() path when the
designator targets a Worker service; minimal standalone repro filed as cloudflare/workerd#6901). The safe local design therefore mirrors Miniflare's existing Hyperdrive SSL-proxy
pattern: keep the designator as external.tcp → 127.0.0.1:<port> and run a small local TCP bridge
on that port which relays bytes over the remote-proxy WebSocket. Verified: no crash, clean relay.
(b) Credentials must be seeded from the edge session. The edge generates fresh dummy
credentials per session, so the local binding's connectionString must reproduce the edge
session's user/password/database (config id) or drivers fail auth after the greeting. Our
prototype adds a tiny guarded endpoint to ProxyServerWorker (MF-HD-Seed + MF-Binding headers →
returns the binding's connectionString once, no logging) and injects those values into the local
binding after the session is established. This is the only genuinely new seam: it needs one async
step in the dev pipeline after maybeStartOrUpdateRemoteProxySession resolves (the current buildMiniflareBindingOptions path is synchronous).
Proposed semantics (maintainer input wanted)
How should users opt in? Options, in our order of preference:
remote: true on the hyperdrive binding, consistent with other bindings. When set, localConnectionString is not required (and ignored); when unset, behavior is exactly today's.
Same as (1) but keep localConnectionString as an explicit fallback if the remote session
cannot be established (more moving parts; unclear it's wanted).
Gate the whole thing behind an --experimental-* flag first.
Scope of the change (all TypeScript, no workerd changes): miniflare hyperdrive plugin
(schema + bridge service via the existing HyperdriveProxyController pattern), the ProxyServerWorker
seed endpoint, hyperdriveEntry/pickRemoteBindings/config validation in wrangler
(hyperdrive: "local-only" → conditional remote), plus the async credential-seeding step.
We're happy to send the PR once there's agreement on the semantics. Tests would extend the
existing remote-bindings-connect.spec.ts harness.
Reproduction / evidence
Spike code is local-only, but the key measurements (masked) were:
SEED: db=<config-id> userLen=32 passLen=32 ssl=disabled
AUTH: GREEN (connection established)
QUERY-RESULT: [{"one":1,"version":"8.0.25"}] # origin version, not the proxy banner
and the edge probe of env.HYPERDRIVE:
connectionString ≈ mysql://<dummy32>:<dummy32>@<hex32>.hyperdrive.local:3306/<config-id>?ssl-mode=disabled
connect()/connect("host:port") → identical MySQL handshake (address ignored)
Which Cloudflare product(s) does this pertain to?
Wrangler, Miniflare (remote bindings), Hyperdrive
Motivation
Hyperdrive bindings are currently
local-onlyin local dev: Miniflare connects directly to theorigin database via
localConnectionString. For databases that are only reachable through aprivate network — e.g. Hyperdrive configs whose origin is a tcp-type VPC Service (#12874) — there
is nothing a developer can put in
localConnectionString, so local dev is simply not possible.Now that raw TCP
connect()tunnelling over remote bindings has landed for VPC Networks/Services(#14712), the same relay can carry Hyperdrive traffic. @xortive suggested exactly this in
#14712 (comment). We prototyped
it end-to-end against the real edge and are proposing a design here before sending a PR
(per CONTRIBUTING's discuss-first guidance), because one part of the semantics deserves
maintainer input.
What we verified empirically (all against a real preview session + real private MySQL)
type-agnostic;
startRemoteProxySession({ HYPERDRIVE: { type: "hyperdrive", id } })works today.env.HYPERDRIVE.connect()in the ProxyServerWorker contextreturns a socket to Hyperdrive's MySQL front-end; the address argument is ignored (same
config-decides-routing model as VPC Services), so the existing
MF-Connect-Addresstunnelendpoint from [miniflare] Support connect() on remote VPC Network bindings in local dev #14712 works unmodified.
connectionStringis fully synthetic on both ends: magic<hex>.hyperdrive.localhost,per-session generated dummy credentials, and the config id in the database slot. The client
authenticates to Hyperdrive with these; Hyperdrive holds the real origin credentials.
edge Hyperdrive → real origin. A stock
mysql2pool authenticated successfully andSELECT VERSION()returned the origin server's version (8.0.25) — i.e. post-auth queriesare proxied to the real database, exactly like production.
Two constraints that shape the design
(a) The Hyperdrive designator must stay
external(tcp). Our first prototype pointed theHyperdrive binding's designator at the shared remote-proxy-client Worker (the D1/VPC pattern) —
workerd segfaults in that configuration (crash in the Hyperdrive JSG
connect()path when thedesignator targets a Worker service; minimal standalone repro filed as cloudflare/workerd#6901). The safe local design therefore mirrors Miniflare's existing Hyperdrive SSL-proxy
pattern: keep the designator as
external.tcp → 127.0.0.1:<port>and run a small local TCP bridgeon that port which relays bytes over the remote-proxy WebSocket. Verified: no crash, clean relay.
(b) Credentials must be seeded from the edge session. The edge generates fresh dummy
credentials per session, so the local binding's
connectionStringmust reproduce the edgesession's user/password/database (config id) or drivers fail auth after the greeting. Our
prototype adds a tiny guarded endpoint to ProxyServerWorker (
MF-HD-Seed+MF-Bindingheaders →returns the binding's
connectionStringonce, no logging) and injects those values into the localbinding after the session is established. This is the only genuinely new seam: it needs one async
step in the dev pipeline after
maybeStartOrUpdateRemoteProxySessionresolves (the currentbuildMiniflareBindingOptionspath is synchronous).Proposed semantics (maintainer input wanted)
How should users opt in? Options, in our order of preference:
remote: trueon the hyperdrive binding, consistent with other bindings. When set,localConnectionStringis not required (and ignored); when unset, behavior is exactly today's.localConnectionStringas an explicit fallback if the remote sessioncannot be established (more moving parts; unclear it's wanted).
--experimental-*flag first.Scope of the change (all TypeScript, no workerd changes): miniflare hyperdrive plugin
(schema + bridge service via the existing
HyperdriveProxyControllerpattern), the ProxyServerWorkerseed endpoint,
hyperdriveEntry/pickRemoteBindings/config validation in wrangler(
hyperdrive: "local-only"→ conditional remote), plus the async credential-seeding step.We're happy to send the PR once there's agreement on the semantics. Tests would extend the
existing
remote-bindings-connect.spec.tsharness.Reproduction / evidence
Spike code is local-only, but the key measurements (masked) were:
and the edge probe of
env.HYPERDRIVE: