pgwasm runs WebAssembly components inside PostgreSQL and exposes
their exports as strongly-typed SQL functions. A component's WIT world is
mapped automatically to PostgreSQL types (records, enums, variants, flags,
domains, lists), its bytes are compiled once and cached on disk, and every
invocation runs inside a Wasmtime sandbox whose
capabilities are configured by the database administrator.
Table of contents
- Why pgwasm
- Quick start
- A minimal component
- Calling it from SQL
- Sandbox and policy
- Documentation
- Development
- License
Why pgwasm
- Components first. Write your UDF in any language that can target a
WebAssembly component
and ship its WIT world.
pgwasmturns each exported function into a regularpg_procrow with real PostgreSQL parameter and return types. - Typed, not buffered. Records become composite types, enums become PG
enums, variants and flags become composite / domain types, lists become
arrays or
bytea. You do not marshal JSON on either side. - Run once, call many. Modules are compiled at
pgwasm.pgwasm_loadtime and their AOT artifacts live under$PGDATA/pgwasm/<module_id>/. Per-call overhead is a pool'd component instance plus argument marshaling. - Strong sandbox. WASI filesystem, sockets, HTTP, and environment
access are all off by default. Administrators widen the ceiling with
pgwasm.*GUCs; per-module overrides can only narrow that ceiling. - Durable, observable. Module metadata is in catalog tables; counters
and gauges live in shared memory;
pgwasm_modules,pgwasm_functions,pgwasm_stats, and related SRFs expose both.
Quick start
# Build and install the extension into the pgrx-managed Postgres (v13 by default). cargo pgrx install --release # Start the pgrx-managed cluster if you are not running your own. cargo pgrx start
In your database:
CREATE EXTENSION pgwasm; -- Optional: make sure the extension is enabled (this is the default). SHOW pgwasm.enabled;
The roles
pgwasm_loader(may load / unload / reload / reconfigure modules) andpgwasm_reader(may readpgwasm_stats) are created byCREATE EXTENSION. Grant membership deliberately; loading a module is a privileged operation.
A minimal component
arith.wit:
package example:arith; world arith { export add: func(a: s32, b: s32) -> s32; }
Compile your language of choice to a WebAssembly component that
implements this world (for example, cargo component build --release
for Rust). You should end up with arith.component.wasm.
Calling it from SQL
-- Load the component from bytes (bytea overload). SELECT pgwasm.pgwasm_load( wasm => pg_read_binary_file('arith.component.wasm'), name => 'arith', options => '{}'::jsonb ) AS module_id; -- The loader creates one pg_proc row per WIT export, named <prefix>_<export>. SELECT arith_add(2, 3); --> 5 -- Inspect everything the extension knows about this module. SELECT module_id, name, abi, generation FROM pgwasm.pgwasm_modules(); SELECT export_id, sql_name, wasm_name, signature FROM pgwasm.pgwasm_functions(); -- Tear it down when you are done. SELECT pgwasm.pgwasm_unload(module_id) FROM pgwasm.pgwasm_modules() WHERE name = 'arith';
The text overload pgwasm.pgwasm_load(path text, ...) is available for file
system loads, subject to pgwasm.allow_load_from_file,
pgwasm.module_path, and pgwasm.allowed_path_prefixes. See
docs/guc.md for the full list.
Sandbox and policy
Every pgwasm.* GUC defaults to the safe option: all WASI surfaces are
off, SPI access is off, fuel is off, and the invocation deadline is 5 s.
To let a module reach the network, an administrator must set both the
master toggle (pgwasm.allow_wasi) and the specific capability
(pgwasm.allow_wasi_net, pgwasm.allow_wasi_http), and populate
pgwasm.allowed_hosts. A module author can still opt to narrow further
-- see the policy / limits keys in the options JSON accepted by
pgwasm.pgwasm_load and pgwasm.pgwasm_reconfigure.
See docs/guc.md for every GUC, default, scope, and hot/cold reconfiguration semantics.
Documentation
- docs/architecture.md — the full v2 design: catalog, shared memory, runtime, trampoline, type mapping, policy, metrics.
- docs/guc.md — every
pgwasm.*GUC with type, default, scope (USERSET/SUSET/POSTMASTER), and whether it can be changed live or requires a restart. - docs/wit-mapping.md — the canonical WIT → PostgreSQL type table with WIT and SQL examples for every kind (primitives, composites, generics, resources).
Development
This is a pgrx extension
pinned to pgrx 0.18 with Wasmtime 44. The repository is a Cargo
workspace; the extension crate is pgwasm/.
Useful commands (see AGENTS.md for the authoritative testing guide):
# Fast host-only type / borrow check. cargo check -p pgwasm # Regress tests: deterministic SQL goldens. pgrx installs the extension. cd pgwasm && cargo pgrx regress --features pg_test # In-backend unit tests (`#[pg_test]`). cargo pgrx test -p pgwasm
Test lanes (host vs backend)
cargo testis a host-only lane and must stay free of direct Postgres backend symbol dependencies.cargo pgrx test -p pgwasmis the backend lane for#[pg_test]and any code paths that require pgrx/Postgres runtime symbols.- For CI, run both lanes explicitly (do not treat one as a substitute for the
other):
cargo testcargo pgrx test pg17 -p pgwasm
When adding or modifying Rust code, follow
.cursor/rules/rust-coding-standards.mdc
(alphabetical #[derive(...)], three-block use layout, alphabetical
Cargo dependency keys, no unwrap() outside tests).
License
pgwasm is licensed under the terms of the LICENSE file at
the root of this repository.