From 081bacf2abe9e6d959acb85831f0f8dbb033798a Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Fri, 3 Jul 2026 14:41:56 +0800 Subject: [PATCH] feat(wasmguest): wire bnwasm Pool into guest CoreServices (WO-WZ-004) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bind the db.* driver over the same transport as the capability stubs so deps.Pool keeps working for plugin sqlc code: - dispatch.go: g.services.Pool = bnwasm.NewPool(capTransport) (nil on native/DESCRIBE → fails cleanly, matching the caps stubs). - hostcalls.go (wasip1): also bind the "bnwasm" database/sql driver's process-global transport to CallHost. - docs/wasm-abi.md: document the implemented guest driver + pgx-primary rationale under the Pool disposition. Co-Authored-By: Claude Fable 5 --- docs/wasm-abi.md | 13 ++++++++++++- plugin/wasmguest/dispatch.go | 6 ++++++ plugin/wasmguest/hostcalls.go | 10 ++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/wasm-abi.md b/docs/wasm-abi.md index 67518ac..19d39aa 100644 --- a/docs/wasm-abi.md +++ b/docs/wasm-abi.md @@ -209,7 +209,18 @@ zero value / best-effort on transport failure. the per-plugin Postgres role. `DbError.code` carries the SQLSTATE. Transactions: `tx_begin` returns an opaque `tx_handle` (never 0); `query`/ `exec` with `tx_handle = 0` run autocommit. Handles die with the call - chain's deadline so a guest can never pin a connection. + chain's deadline so a guest can never pin a connection. **Guest side + (WO-WZ-004):** `core/plugin/wasmguest/bnwasm` implements this over the + transport as two surfaces — a `database/sql` driver registered as `"bnwasm"`, + and a `plugin.Pool` handing out a `pgx.Tx`-shaped value. The latter is the + primary path: current plugins' sqlc configs use `sql_package: "pgx/v5"`, so + their generated `DBTX` needs `pgconn.CommandTag`/`pgx.Rows`/`pgx.Row` (which + `database/sql` cannot produce), and the `Pool`/`Tx` satisfy it with no source + edits. `DbError` surfaces as `*pgconn.PgError` (SQLSTATE preserved for + `errors.As`). Named args (`pgx.NamedArgs`/`QueryRewriter`) and nested + transactions/savepoints are rejected with clear errors — no fleet plugin uses + either. The DbValue↔Go scan mapping is pinned in the exported + `bnwasm.DbValueFixtures` table, which the WO-WZ-007 host executor mirrors. - `Interceptors` (`connect.Option`) → host-side only; RBAC merges from `manifest.rbac_method_roles`. - `AppURL` / `MediaPath` → delivered once in `LoadRequest.host_config`. diff --git a/plugin/wasmguest/dispatch.go b/plugin/wasmguest/dispatch.go index c32c942..8366344 100644 --- a/plugin/wasmguest/dispatch.go +++ b/plugin/wasmguest/dispatch.go @@ -12,6 +12,7 @@ import ( abiv1 "git.dev.alexdunmow.com/block/core/abi/v1" "git.dev.alexdunmow.com/block/core/plugin" + "git.dev.alexdunmow.com/block/core/plugin/wasmguest/bnwasm" "git.dev.alexdunmow.com/block/core/plugin/wasmguest/caps" "github.com/google/uuid" "google.golang.org/protobuf/proto" @@ -68,6 +69,11 @@ func newGuest(reg plugin.PluginRegistration) *guest { templates: newCaptureTemplateRegistry(), } g.services = caps.NewCoreServices(capTransport) + // Pool is left zero by NewCoreServices (it does not cross as a capability + // call); bind the db.* driver over the same transport so deps.Pool keeps + // working for plugin sqlc code. A nil transport (native/DESCRIBE) yields a + // Pool whose calls fail cleanly, matching the capability stubs. + g.services.Pool = bnwasm.NewPool(bnwasm.Transport(capTransport)) g.rag, _ = g.services.RAGService.(*caps.RAGStub) g.registerErr = runRegister(reg, g.templates, g.blocks) return g diff --git a/plugin/wasmguest/hostcalls.go b/plugin/wasmguest/hostcalls.go index d7d7b0f..cdd88d2 100644 --- a/plugin/wasmguest/hostcalls.go +++ b/plugin/wasmguest/hostcalls.go @@ -7,6 +7,7 @@ import ( "runtime" abiv1 "git.dev.alexdunmow.com/block/core/abi/v1" + "git.dev.alexdunmow.com/block/core/plugin/wasmguest/bnwasm" "google.golang.org/protobuf/proto" ) @@ -28,8 +29,13 @@ func hostCall(ptr uint32, size uint32) uint64 // init binds the capability-stub transport (package caps) to the real // host_call-backed CallHost. Only wasip1 has a host to call; native builds -// leave caps' transport nil, so their capability calls fail cleanly. -func init() { capTransport = CallHost } +// leave caps' transport nil, so their capability calls fail cleanly. The same +// transport backs the "bnwasm" database/sql driver for plugins that open a +// *sql.DB (the pgx-flavored plugin.Pool path binds capTransport in dispatch). +func init() { + capTransport = CallHost + bnwasm.SetDefaultTransport(bnwasm.Transport(CallHost)) +} // HostError is a failed capability call's AbiError surfaced as a Go error. type HostError struct {