Skip to content

Repository files navigation

wasipg

Embedded PostgreSQL for Go — a WASI build of PostgreSQL running in-process under wazero, with working error recovery. Pure Go: no cgo, no Docker, no native binaries.

Status: pre-alpha. The engine works end-to-end (PostgreSQL 17.5, real pgx clients, erroring queries recovered in-instance — see the conformance suite), but no bundle release is published yet and APIs may change without notice.

Usage

eng, err := wasipg.Start(ctx, wasipg.Options{
	// Until the first release is published, point at artifacts from
	// ./build/build.sh; a released wasipg.DefaultBundle (pinned
	// version + sha256, auto-fetched and cached) replaces this.
	Bundle: wasipg.LocalBundle("build/out"),
})
if err != nil { /* ... */ }
defer eng.Close()

conn, err := pgx.ConnectConfig(ctx, eng.PGXConfig())

SELECTs, DDL, transactions, and — the whole point — erroring queries behave like a real PostgreSQL: a syntax error or constraint violation comes back as a normal wire-level ERROR and the same instance keeps serving. Engines are single-connection and disposable (wasipg.NewPool reboots fresh ones from the pristine bundle); data is ephemeral unless you pass Options.Dir.

Note that the engine is not a security boundary: it runs in your process and the wire-level password handshake is not actually verified by the upstream WASI backend. Treat it like SQLite, not like a network-exposed server.

Why

PGlite proved PostgreSQL runs well as WASM, but its WASI builds compile with setjmp/longjmp disabled: every PostgreSQL ERROR escalates to abort() and kills the whole instance. That makes them unusable for any workload where erroring queries are normal input (tests, dev tools, anything driven by user SQL). The JS/Emscripten builds recover; the WASI builds do not — and there is no maintained, error-recovering embedded PostgreSQL for Go at all.

wasipg's bet: PostgreSQL's own error recovery (sigsetjmp-based, and fully functional in single-user mode on native builds) works in WASI too, once the build enables wasi-sdk's setjmp/longjmp support — which lowers onto the standardized wasm exception-handling proposal — and the runtime implements that proposal, which wazero does as of v1.12.

The stage-0 gate (passed, 2026-08)

gate/ pins the toolchain/runtime contract with a minimal reproduction of PostgreSQL's error-recovery shape: a long-lived jmp_buf, errors raised deep in the call stack via longjmp, control returning to a top-level loop that keeps serving.

Piece Requirement
Compiler wasi-sdk ≥ 33 (LLVM 22): -mllvm -wasm-enable-sjlj -mllvm -wasm-use-legacy-eh=false + -lsetjmp
EH flavor standardized exnref (try_table/throw_ref) — LLVM's default legacy opcodes are rejected by wazero
Runtime wazero ≥ 1.12: WithCoreFeatures(api.CoreFeaturesV2 | experimental.CoreFeaturesExceptionHandling)
$ go test ./gate/
ok  	github.com/moznion/wasipg/gate

Roadmap

The full implementation plan — work items, exit criteria, risks, and fallbacks per stage — lives in docs/PLAN.md. Summary:

  1. Bundle build (done) — a self-contained WASI PostgreSQL 17.5 bundle (pglite.wasi + runtime FS + pristine initdb'd data dir) built reproducibly from electric-sql/postgres-pglite, Electric's maintained PostgreSQL fork: ./build/build.sh, then go run ./cmd/smoke boots it under wazero and drives it with pgx (boot from pristine data: ~50 ms).
  2. sjlj build (done) — the same bundle built with setjmp/longjmp lowered onto standardized exnref exception handling (wasi-sdk 33). A real pgx client gets wire-level ERRORs (correct SQLSTATEs) and the same instance keeps serving — 120 mixed error/success iterations without a trap, aborted-transaction semantics intact (go test ./conformance/). Cost vs the non-sjlj build: +0.2% module size, no measurable boot/latency change.
  3. Go library (mostly done)wasipg.Start / Engine / PGXConfig / Pool with sha256-verified bundle fetch and caching; conformance suite in CI (Linux/macOS). Remaining: first tagged release publishing the bundle (which fills in wasipg.DefaultBundle) and wiring into a real consumer.

Design intent: the bundle-loading seam stays abstract so artifacts can later be swapped for official libpglite releases when those ship.

Non-goals (initial)

  • Multiple concurrent connections (the engine is single-connection; pool instances instead).
  • Durability guarantees for long-lived data. The first target workloads are ephemeral: tests, dev databases, static analysis oracles.
  • Extensions beyond what the bundle ships (initially plpgsql only).

Acknowledgements

This project stands on PGlite by Electric — specifically the postgres-pglite patch set that makes single-process PostgreSQL speak the wire protocol — and on pmp-p's pioneering WASI build work. PostgreSQL itself is © the PostgreSQL Global Development Group, under the PostgreSQL License.

License

Go code and build recipes: Apache-2.0 (see LICENSE).

PostgreSQL and postgres-pglite-derived material remains under its upstream licenses and is not covered by that grant: the released bundle artifacts are PostgreSQL 17.5 builds under the PostgreSQL License (shipped as COPYRIGHT inside pglite-wasi.tar.xz and attached to each release), and the diffs under build/patches/ are modifications of that source (see build/patches/README.md). NOTICE carries the full attribution, including the statically linked zlib and wasi-libc.

About

Embedded PostgreSQL for Go — a WASI build of PostgreSQL running in-process under wazero, with working error recovery

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages