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.
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.
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.
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/gateThe full implementation plan — work items, exit criteria, risks, and fallbacks per stage — lives in docs/PLAN.md. Summary:
- 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, thengo run ./cmd/smokeboots it under wazero and drives it with pgx (boot from pristine data: ~50 ms). - 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. - Go library (mostly done) —
wasipg.Start/Engine/PGXConfig/Poolwith sha256-verified bundle fetch and caching; conformance suite in CI (Linux/macOS). Remaining: first tagged release publishing the bundle (which fills inwasipg.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.
- 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).
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.
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.