@di-framework/pglite-component 6.0.14 → 6.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,6 +65,32 @@ It fails if the final artifact imports sockets or leaves the engine unresolved.
65
65
  Ship `dist/di-framework-pglite.wasm` together with `THIRD-PARTY-NOTICES.txt`.
66
66
  Build provenance and checksums are in `dist/BUILD-INFO.txt` and `SHA256SUMS`.
67
67
 
68
+ On one host architecture and one builder image, two clean engine builds are
69
+ byte-identical. `scripts/build-engine.ts` sets `SOURCE_DATE_EPOCH` to the commit
70
+ time of the pinned engine recipe (`PGLITE_SOURCE_REF`) and passes it into the
71
+ build container. OpenSSL then writes that date into `crypto/buildinf.h`
72
+ instead of the wall clock, so `libcrypto.a` is deterministic, and clang's
73
+ `__DATE__`/`__TIME__` are fixed. `engine/build-pgcrypto.sh` fails if
74
+ `buildinf.h` does not carry the pinned date. The engine does not link
75
+ OpenSSL's build info today, so this guards against it being linked in later.
76
+ `make reproducible` builds the engine twice from clean copies of the package
77
+ and fails unless `pglite.wasi`, `engine.core.wasm`, `engine.wasm` and every
78
+ embedded support file have the same sha256. The builds go in a new
79
+ `df-pglite-repro.*` directory under the system temp dir, or under
80
+ `REPRO_ARGS="--work-dir DIR"`, an existing directory outside the repository checkout.
81
+ Only that new directory is deleted, and it is kept if a build fails or the
82
+ digests differ. `--sdk-cache DIR` reuses SDK tarballs you have already
83
+ downloaded, and `--keep` keeps both builds. The
84
+ [reproducibility workflow](../../.github/workflows/pglite-engine-reproducible.yml)
85
+ runs the check on x86_64 when an engine input changes.
86
+
87
+ The results are not portable across these limits:
88
+ - An aarch64 host and an x86_64 host produce different engines and support
89
+ files, so an Apple Silicon build does not reproduce the released x86_64
90
+ engine.
91
+ - The builder image's `FROM debian:12` tag and its `apt-get` packages are not
92
+ pinned, so builds made months apart can differ.
93
+
68
94
  `make smoke` composes a real WIT consumer, runs it without network access, and
69
95
  checks typed parameters/results, SQL errors and recovery, transactions,
70
96
  rollback on close/drop, exclusive connection ownership, durability settings,
@@ -79,7 +105,7 @@ The pglite component is published to GitHub Container Registry (GHCR) as an OCI
79
105
  The workflow has four jobs:
80
106
  - `check-source` (`packages: read`) hashes the committed input files (`scripts/source-check.ts`). On a push it skips the build when `ghcr.io/di-framework/pglite-component:src-<hash>` already exists, so commits that do not change the component publish nothing.
81
107
  - `build` (no write token) runs the same targets as `ci.yml`: `make tools-rust`, `make unit smoke` (which runs `make build`, composing the provider with the engine via `wac plug`), then the publisher crates' clippy and tests.
82
- - `publish` (`packages: write`) downloads the tested `dist/` and runs `pglite-component-publisher`. It never rebuilds or modifies `dist/`. It checks `SHA256SUMS`, requires the export `di-framework:pglite/database@0.2.0` and rejects any import that is not `wasi:*`, then pushes `ghcr.io/di-framework/pglite-component:<commit sha>` once, pulls it back by the manifest digest to compare bytes, then points the version tag (`0.2.0`, the WIT package version) at that same manifest with `oras tag` and asserts both tags resolve to one digest. The `build` job passes the tested wasm sha256 to `publish`, which verifies it before pushing. It also tags the manifest `src-<hash>`.
108
+ - `publish` (`packages: write`) downloads the tested `dist/` and runs `pglite-component-publisher`. It never rebuilds or modifies `dist/`. It checks `SHA256SUMS`, requires the export `di-framework:pglite/database@0.2.0` and rejects any import that is not `wasi:*`, then pushes the layer, config and manifest with `oras` under `ghcr.io/di-framework/pglite-component:<commit sha>` only, pulls it back with `wash` by the manifest digest to compare bytes, and only then points the version tag (`0.2.0`, the WIT package version) at that same manifest with `oras tag` and asserts both tags resolve to one digest. A failed check leaves the version tag where it was. The manifest and config carry a fixed `created` time (the publish step clears `SOURCE_DATE_EPOCH`, so `1970-01-01T00:00:00Z`), so publishing an unchanged `di-framework-pglite.wasm` gives the same digest and the pin job has nothing to pin. The `build` job passes the tested wasm sha256 to `publish`, which verifies it before pushing. It also tags the manifest `src-<hash>`.
83
109
  - `pin` (`contents: write`, `pull-requests: write`) opens or updates the PR `bot/pin-pglite-component`, "chore: pin pglite-component to <digest>", which rewrites the digests in this README with `scripts/pin-artifact.ts`. GitHub does not run workflows on a PR that `GITHUB_TOKEN` opened, so a maintainer has to start its checks (close and reopen it) before merging. It also runs when a push's source is already published (a revert to an older source, or a pin PR closed unmerged), so `main`'s pin follows the current source; it opens no PR when `main` already has the digest. On that path the `realign` job (`packages: write`) points the moving version tag back at that source's digest with `scripts/retag.sh`, which leaves a tag that is already there alone. Every tag move is behind the same source guard, so a stale run never retags.
84
110
 
85
111
  Tags are movable. `0.2.0` always points to the latest `main` publish for WIT package 0.2.0, and re-running a commit also moves its SHA tag. Only the `@sha256:...` digest is immutable, so consumers pin by digest.
@@ -87,7 +113,7 @@ Tags are movable. `0.2.0` always points to the latest `main` publish for WIT pac
87
113
  ### Artifact location
88
114
 
89
115
  The digest is in **Details** > **Record component digest** > **Step summary**, and in the `pglite-publish-report` artifact (`publish-report.json`).
90
- Published digest: `ghcr.io/di-framework/pglite-component@sha256:4c6243bed7c30df93ef5a6c11c3070e3e777786fe4e34b3a63f4b89b8f75a523`
116
+ Published digest: `ghcr.io/di-framework/pglite-component@sha256:42a5a38f61abb621643c8175fd4c9a8652778eb90edc458eb355625733fa4c33`
91
117
 
92
118
  **Note:** The GHCR package must be made public by an org admin after the first publish. Until then, only members with access to di-framework/platform can pull it.
93
119
 
@@ -125,7 +151,7 @@ wasmtime run --dir ./data::/data path/to/app.composed.wasm
125
151
  To use the published OCI component from GHCR instead (pin by digest for reproducibility):
126
152
 
127
153
  ```sh
128
- export PGLITE_REF='ghcr.io/di-framework/pglite-component@sha256:4c6243bed7c30df93ef5a6c11c3070e3e777786fe4e34b3a63f4b89b8f75a523'
154
+ export PGLITE_REF='ghcr.io/di-framework/pglite-component@sha256:42a5a38f61abb621643c8175fd4c9a8652778eb90edc458eb355625733fa4c33'
129
155
  wash oci pull "${PGLITE_REF}" pglite.wasm
130
156
  wac plug --plug pglite.wasm path/to/app.wasm -o path/to/app.composed.wasm
131
157
  mkdir -p ./data
@@ -210,13 +236,25 @@ PostgreSQL type:
210
236
  | `float4`, `float8` | `real` | |
211
237
  | `bytea` | `blob` | |
212
238
  | `json`, `jsonb` | `json` | JSON text; `jsonb` is re-rendered by PostgreSQL (`{"a": 1}`) |
213
- | `timestamptz` | `timestamp` | RFC 3339 in UTC, e.g. `2024-01-15T10:30:00Z`, fractional seconds kept (`...:00.123Z`). `infinity` and `-infinity` are returned as those literals; BC dates are returned unmodified (`0044-03-15 00:00:00+00 BC`) |
239
+ | `timestamptz` | `timestamp` | RFC 3339 in UTC, e.g. `2024-01-15T10:30:00Z`, fractional seconds kept (`...:00.123Z`). `infinity` and `-infinity` are returned as those literals. A value RFC 3339 cannot hold (BC dates, years after 9999) is `text`, as PostgreSQL printed it (`0044-03-15 00:00:00+00 BC`) |
214
240
  | `timestamp` (no zone) | `text` | `2024-01-15 10:30:00` (ISO, space separator, no offset). Deliberately not `timestamp`, which always denotes an instant |
215
241
  | `date` | `text` | `2024-01-15` |
216
242
  | `uuid`, `numeric` | `text` | canonical PostgreSQL text (`12.50` keeps its scale) |
217
243
  | arrays | `text` | PostgreSQL array literal, e.g. `{a,b}` |
218
244
  | everything else | `text` | |
219
245
 
246
+ A caller can still change either setting with `SET` for the rest of the connection. `timestamptz`
247
+ cells are parsed rather than trusted, so the `timestamp` variant always holds RFC 3339 in UTC (or
248
+ an infinity):
249
+
250
+ - After `SET TimeZone`, with `DateStyle` still ISO, PostgreSQL prints a local offset
251
+ (`2024-01-15 05:30:00.5-05`). The provider converts it back to UTC (`2024-01-15T10:30:00.5Z`).
252
+ - After `SET DateStyle` to `SQL`, `Postgres` or `German`, the output cannot be converted reliably
253
+ (`15/01/2024 05:30:00.5 EST`), so it is returned as `text`, exactly as printed. `infinity` and
254
+ `-infinity` print the same under every DateStyle, so they stay `timestamp`.
255
+
256
+ Closing and reopening the connection pins `DateStyle=ISO` and `TimeZone=UTC` again.
257
+
220
258
  Parameters are typed as follows. `integer`, `real`, `boolean` and `blob` are
221
259
  sent as `int8`, `float8`, `bool` and `bytea`. `text`, `json` and `timestamp`
222
260
  are all sent with OID 0 (unknown), so PostgreSQL infers the type from context:
@@ -269,6 +307,28 @@ engine routes to the host's WASI `random` interface. `gen_random_uuid()` is
269
307
  built into PostgreSQL and works without the extension. Other contrib modules
270
308
  are not included. The WIT interface does not change.
271
309
 
310
+ Public-key PGP (`pgp_pub_encrypt`/`pgp_pub_decrypt`) works with ElGamal and RSA
311
+ keys; pgcrypto cannot generate keys, so import them with `dearmor()`. For
312
+ ElGamal, the ephemeral `k` comes from OpenSSL's DRBG (`BN_rand`), not from
313
+ `pg_strong_random`. On WASI, OpenSSL seeds that DRBG from `getentropy`, which
314
+ is the same WASI `random_get` import. It fails closed: if the host cannot
315
+ supply entropy, the DRBG does not start and `pgp_pub_encrypt` raises an error
316
+ instead of using a weak `k`.
317
+
318
+ Blowfish (`bf`), CAST5 (`cast5`) and single DES (`des`) are unavailable in
319
+ this build, because the OpenSSL legacy provider is not built or loaded.
320
+ pgcrypto refuses them with `Cipher cannot be initialized`: in `encrypt()` and
321
+ `decrypt()`, in the PGP functions, and when decrypting PGP messages that use
322
+ them. `3des` (DES-EDE3) is in OpenSSL's default provider and still works, for
323
+ legacy data only. Use `aes` (`aes128`, `aes192`, `aes256` for PGP). Password hashing with
324
+ `crypt()` and `gen_salt('bf')`, `'md5'`, `'xdes'` or `'des'` is unaffected,
325
+ because pgcrypto implements those itself.
326
+
327
+ The engine has no `dlopen`. Its emulation keeps a fixed table of 64 loaded
328
+ libraries per engine instance. pgcrypto resolves before that table and never
329
+ uses a slot. A `LOAD` of a new library after the table is full fails with
330
+ `dlopen table is full` and leaves the session usable.
331
+
272
332
  ## Persistence and ownership
273
333
 
274
334
  For an `open("/data/orders", ...)` call:
@@ -318,7 +378,8 @@ annotations provide storage; they do not enforce database ownership.
318
378
  - `src/pgwire.rs`: PostgreSQL message encoding and typed rows, over memory.
319
379
  - `src/engine.rs`: persistent filesystem setup and exclusive engine access.
320
380
  - `engine/bridge.wat`: private canonical ABI bridge to the core engine.
321
- - `engine/build-pgcrypto.sh`: builds OpenSSL libcrypto and pgcrypto in the engine container and generates pgcrypto's static `dlsym` table; `scripts/patch-engine-source.ts` links both into the engine.
381
+ - `engine/build-pgcrypto.sh`: builds OpenSSL libcrypto and pgcrypto in the engine container and generates pgcrypto's static `dlsym` table; `scripts/patch-engine-source.ts` links both into the engine. The libcrypto build is cached under a key of the OpenSSL source tarball's sha256, the Configure options and `SOURCE_DATE_EPOCH`, so an OpenSSL version bump always rebuilds it.
382
+ - `scripts/patch-engine-source.ts`: also replaces the upstream `dlopen` emulation's unbounded table (`dltab`) with the bounded one described under [Extensions](#extensions), and keeps pgcrypto's hook ahead of it.
322
383
  - `scripts/prepare-engine.ts`: pinned engine preparation and componentization (all build scripts are Bun TypeScript; see `scripts/`).
323
384
  - `wit/deps/pglite-engine/engine.wit`: internal interface, composed away at build.
324
385
 
@@ -1,6 +1,6 @@
1
1
  component: di-framework:pglite@0.2.0
2
2
  profile: release
3
- built: 2026-10-13T22:17:05Z
3
+ built: 2026-10-16T03:00:39Z
4
4
  rustc: rustc 1.97.1 (8bab26f4f 2026-07-14)
5
5
  target: wasm32-wasip2
6
6
  wasm-tools: wasm-tools 1.258.0 (5c6d31c78 2026-08-24)
@@ -10,5 +10,5 @@ engine: PostgreSQL 17.5, persistent WASI filesystem
10
10
  extensions: plpgsql, pgcrypto (OpenSSL 3.5.9 libcrypto)
11
11
  engine-source: https://github.com/moznion/wasipg/tree/fa290be67946c0d3c4540bd1400b11e193d874e1
12
12
  adapter-sha256: e51dd61669a20daca34befe7fdadf1ac84e4795a2c84a8400818ee1de5373ccc
13
- engine-sha256: e0afe7bc67537c40ae37655df4722987e968784c45a3684ed35df2f19d86bdbf
14
- size: 20112680 bytes
13
+ engine-sha256: ad2d96c9ce85a35620719fbc6a038c407234d11c5a106e70c2863b104dcfa52c
14
+ size: 20116132 bytes
package/dist/SHA256SUMS CHANGED
@@ -1,4 +1,4 @@
1
- 269169e596bc15f4cb5539bc9af85e58f6ac9bb090dd1ba05254878768c6b823 di-framework-pglite.wasm
1
+ bff475faba7eecde56427e40704c282523e0f84482389e029f9dd58d9cf0f6aa di-framework-pglite.wasm
2
2
  6053154ec1bc94de63ce4f4f4cce5922ef57dce794c2dde326f5845b8982d35f di-framework-pglite.wit
3
- 11f6ed49c8b63158770f1a4dea98780424ed9759ffa80a883f9efa5993bb7d29 BUILD-INFO.txt
4
- f57bcfb7d092565405081e910da9816b6b6a23d916c28542bf5d469e4bd31ff7 THIRD-PARTY-NOTICES.txt
3
+ f7a7643d858d330dc4e114d63b554e15dcacfcf19c5afae6454b456e33216b12 BUILD-INFO.txt
4
+ e19368fcd3dc22d06c46a5256950924b3775154cd77f7be2d3105036be576619 THIRD-PARTY-NOTICES.txt
@@ -20,7 +20,10 @@ ABI bridge, uses WASI randomness instead of a /dev/urandom preopen, and keeps
20
20
  bootstrap files within the database's persistent filesystem mount. A growable,
21
21
  bounded memory buffer replaces socket-file spillover for query replies. Backend
22
22
  startup restores application session defaults after bootstrap and enables
23
- durable writes.
23
+ durable writes. The emulated dlopen keeps loaded libraries in a bounded table
24
+ instead of writing past the end of an unsized array. pgcrypto's PGP CFB mode
25
+ (contrib/pgcrypto/pgp-cfb.c) is patched to refuse ciphers that are unavailable
26
+ in this build.
24
27
 
25
28
  The engine's core module, support files, adapter, and the provider are
26
29
  composed into one deployable artifact. Engine code executes in the Wasm
@@ -118,6 +121,7 @@ distribution.
118
121
  px.c, px-hmac.c, px-crypt.c, mbuf.c, openssl.c, pgcrypto.c, pgp*.c and
119
122
  their headers:
120
123
 
124
+ Copyright (c) 2000 Marko Kreen
121
125
  Copyright (c) 2001 Marko Kreen
122
126
  Copyright (c) 2005 Marko Kreen
123
127
  All rights reserved.
Binary file
@@ -100,7 +100,8 @@ export declare function toWasmPgValue(value: unknown): WasmPgValue;
100
100
  /**
101
101
  * Formats a `timestamp` variant the way the `wasmcloud:postgres` codec does:
102
102
  * `YYYY-MM-DDTHH:MM:SS.mmmZ`. Microseconds are truncated to milliseconds.
103
- * `infinity`, `-infinity`, and BC dates are returned unchanged.
103
+ * `infinity` and `-infinity` are returned unchanged. (The provider returns BC dates and other
104
+ * values it cannot express in RFC 3339 as `text`, so they never reach this function.)
104
105
  */
105
106
  export declare function pgliteTimestampIso(value: string): string;
106
107
  /** Converts a `value` variant into what the `wasmcloud:postgres` codec in `@di-framework/repo` returns. */
@@ -141,7 +141,8 @@ const RFC3339_UTC = /^(\d{4,}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})(?:\.(\d+))?Z$/;
141
141
  /**
142
142
  * Formats a `timestamp` variant the way the `wasmcloud:postgres` codec does:
143
143
  * `YYYY-MM-DDTHH:MM:SS.mmmZ`. Microseconds are truncated to milliseconds.
144
- * `infinity`, `-infinity`, and BC dates are returned unchanged.
144
+ * `infinity` and `-infinity` are returned unchanged. (The provider returns BC dates and other
145
+ * values it cannot express in RFC 3339 as `text`, so they never reach this function.)
145
146
  */
146
147
  export function pgliteTimestampIso(value) {
147
148
  const match = RFC3339_UTC.exec(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@di-framework/pglite-component",
3
- "version": "6.0.14",
3
+ "version": "6.0.15",
4
4
  "type": "module",
5
5
  "description": "Embedded PostgreSQL component with persistent WASI storage and no TCP, plus its SqlDatabase adapter for @di-framework/repo.",
6
6
  "license": "(MIT OR Apache-2.0)",
package/wit/world.wit CHANGED
@@ -60,7 +60,12 @@ interface types {
60
60
  /// - `json` and `jsonb` -> `json` (JSON text).
61
61
  /// - `timestamptz` -> `timestamp`, RFC 3339 in UTC (`2024-01-15T10:30:00Z`,
62
62
  /// fractional seconds kept). `infinity` and `-infinity` are returned as
63
- /// those literals.
63
+ /// those literals. This holds after a caller's own `SET TimeZone`: any
64
+ /// ISO output is converted back to UTC. A value that cannot be converted
65
+ /// (BC dates, years after 9999, or any output after `SET DateStyle` to
66
+ /// `SQL`, `Postgres` or `German`) is returned as `text`, as PostgreSQL
67
+ /// printed it. `infinity` and `-infinity` stay `timestamp` under every
68
+ /// DateStyle. Reopening the connection pins both settings again.
64
69
  /// - `timestamp` (no zone) and `date` -> `text` (`2024-01-15 10:30:00`,
65
70
  /// `2024-01-15`).
66
71
  /// - `uuid` and `numeric` -> `text`; arrays -> `text` as PostgreSQL array