From 7ab1a73400e1a4878efdc6ef001a593e286a4f6a Mon Sep 17 00:00:00 2001 From: "dobby-yivi-agent[bot]" <275734547+dobby-yivi-agent[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 23:01:19 +0000 Subject: [PATCH 1/3] docs: add pg-node example page (from encryption4all/postguard-examples#46) New Node.js example added in postguard-examples. Adds the page, sidebar entry, and overview row. --- docs/.vitepress/config.mts | 1 + docs/repos/overview.md | 1 + docs/repos/pg-node.md | 79 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 docs/repos/pg-node.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 729aefe..a879385 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -93,6 +93,7 @@ export default withMermaid(defineConfig({ collapsed: false, items: [ { text: 'pg-sveltekit', link: '/repos/pg-sveltekit' }, + { text: 'pg-node', link: '/repos/pg-node' }, { text: 'pg-dotnet', link: '/repos/pg-dotnet' }, { text: 'pg-manual', link: '/repos/pg-manual' }, ], diff --git a/docs/repos/overview.md b/docs/repos/overview.md index 69f2b68..aaf466e 100644 --- a/docs/repos/overview.md +++ b/docs/repos/overview.md @@ -42,6 +42,7 @@ From the [postguard-examples](https://github.com/encryption4all/postguard-exampl | Project | Language | Description | |---|---|---| | [pg-sveltekit](/repos/pg-sveltekit) | TypeScript | SvelteKit web app using `@e4a/pg-js` | +| [pg-node](/repos/pg-node) | JavaScript | Node.js CLI using `@e4a/pg-js` from a server runtime | | [pg-dotnet](/repos/pg-dotnet) | C# | .NET console app using `E4A.PostGuard` | | [pg-manual](/repos/pg-manual) | JavaScript | Browser example using `@e4a/pg-wasm` directly (no SDK) | diff --git a/docs/repos/pg-node.md b/docs/repos/pg-node.md new file mode 100644 index 0000000..33fa7ed --- /dev/null +++ b/docs/repos/pg-node.md @@ -0,0 +1,79 @@ +# pg-node + +[GitHub](https://github.com/encryption4all/postguard-examples/tree/main/pg-node) · JavaScript · Node.js Example + +A plain Node.js CLI example showing how to use [`@e4a/pg-js`](/repos/postguard-js) from a server runtime. Part of the [postguard-examples](https://github.com/encryption4all/postguard-examples) repository. + +Mirrors the [pg-sveltekit](/repos/pg-sveltekit) "Informatierijk notificeren" flow (citizen exact-email recipient + organisation email-domain recipient) as a CLI script. + +It shows two modes: + +1. **Encrypt and Send**: Encrypts the input files for both recipients, uploads to Cryptify, and asks Cryptify to email each recipient a download link. +2. **Encrypt and Upload**: Same upload, but silent. Cryptify returns a UUID you can distribute through some other channel. + +## Prerequisites + +- **Node.js 22+**, matching the example's `engines.node`. The SDK itself supports Node 20.3+, Bun, and Deno (see [postguard-js > Server-side usage](/repos/postguard-js#server-side-usage)). +- A PostGuard for Business API key. + +## Setup + +```bash +cd pg-node +npm install +cp .env.example .env +# edit .env: set at minimum PG_API_KEY +``` + +Run one of the two modes: + +```bash +npm run send # encrypt + upload + ask Cryptify to send mails +npm run upload # encrypt + upload silently, no mails +``` + +The script prints the resulting `uuid` and the corresponding `/download?uuid=...` URL. + +## Configuration + +| Variable | Description | Default | +| ----------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------- | +| `PG_API_KEY` | PostGuard for Business API key (`PG-...`) | *(required)* | +| `PG_PKG_URL` | PostGuard PKG server URL | `https://pkg.staging.postguard.eu` | +| `PG_CRYPTIFY_URL` | Cryptify file-sharing URL | `https://storage.staging.postguard.eu` | +| `PG_DOWNLOAD_URL` | PostGuard website used in `/download` URLs | `https://staging.postguard.eu` on staging Cryptify, else `https://postguard.eu` | +| `PG_CITIZEN_EMAIL` | Citizen recipient (exact email match) | `citizen@example.com` | +| `PG_ORGANISATION_EMAIL` | Organisation recipient (matches by domain) | `noreply@example.org` | +| `PG_MESSAGE` | Optional unencrypted body for Cryptify's notify mail | *(empty)* | +| `PG_INPUT_FILES` | Comma-separated file paths to encrypt | two in-memory demo files | + +The default `PG_CRYPTIFY_URL` is the staging deployment. Staging Cryptify does not actually deliver notification emails, so `npm run send` succeeds without spamming real inboxes while you integrate. The upload still returns a real UUID and the download URL is usable. + +## How it maps to the SDK + +The encryption code lives in `src/encryption.mjs`. The send mode passes a `notify` object to opt into Cryptify-sent emails: + +```js +const sealed = pg.encrypt({ + files, + recipients: [ + pg.recipient.email(citizen.email), + pg.recipient.emailDomain(organisation.email), + ], + sign: pg.sign.apiKey(apiKey), + onProgress, + signal, +}); + +const result = await sealed.upload({ + notify: { + recipients: true, + message: message || undefined, + language: 'EN', + }, +}); +``` + +[Source: encryption.mjs#L13-L40](https://github.com/encryption4all/postguard-examples/blob/0fb7789560595d29d28fcf4222e67dc1ab887c2e/pg-node/src/encryption.mjs#L13-L40) + +The upload mode calls `sealed.upload()` with no options. The upload is silent by default; pass `notify` only when you want Cryptify to send the recipient mail. See [JS SDK > Notify options](/sdk/js-encryption#notify-options) for the full shape. From 634dbde958574443b61f41574478a6e772e7610d Mon Sep 17 00:00:00 2001 From: "dobby-yivi-agent[bot]" <275734547+dobby-yivi-agent[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 23:02:13 +0000 Subject: [PATCH 2/3] docs: cover Node 20.3+/Bun/Deno + notify validator (from encryption4all/postguard-js#76, #77) - Add Server-side usage section to postguard-js page - Note upfront YiviSessionError for non-DOM runtimes - Mention validateUploadOptions and the silent-notify console.info --- docs/repos/postguard-js.md | 10 +++++++++- docs/sdk/js-encryption.md | 4 ++++ docs/sdk/js-errors.md | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/repos/postguard-js.md b/docs/repos/postguard-js.md index fb5c522..47bdbe4 100644 --- a/docs/repos/postguard-js.md +++ b/docs/repos/postguard-js.md @@ -262,11 +262,19 @@ try { } ``` +## Server-side usage + +The SDK runs on browsers, Node.js (20.3+), Bun, and Deno. The lower bound is set by `AbortSignal.any`, listed in `engines.node` of the package. + +Encrypt and upload calls work identically across all four runtimes. Decryption with `pg.sign.yivi(...)` and `opened.decrypt({ element })` is browser-only — both render the Yivi QR widget into a DOM element. Calling `pg.sign.yivi(...)` from a server runtime throws `YiviSessionError` upfront before any session starts; pick `pg.sign.apiKey(...)` or `pg.sign.session(...)` instead. + +See the [pg-node example](/repos/pg-node) for a runnable Node.js script using `pg.sign.apiKey`. + ## Development ### Prerequisites -- Node.js 20+ +- Node.js 20.3+ (or Bun / Deno) ### Building diff --git a/docs/sdk/js-encryption.md b/docs/sdk/js-encryption.md index 157369b..5d88435 100644 --- a/docs/sdk/js-encryption.md +++ b/docs/sdk/js-encryption.md @@ -220,6 +220,10 @@ The upload is silent by default. Both recipient and sender mails are opt-in. Pas | `message` | `string` | `undefined` | Optional unencrypted text included in any mail sent | | `language` | `'EN' \| 'NL'` | `'EN'` | Notification email template language | +The SDK validates the `notify` shape and throws `TypeError` for common misuse like `{ notify: true }`, a top-level `recipients`, or non-boolean values such as `{ recipients: 'yes' }`. Catch this in tests rather than at runtime. + +If `notify` is omitted on the first `sealed.upload()` for a given `PostGuard` instance, the SDK logs a one-time `console.info` reminding you that the upload is silent and how to opt in. Pass `{ recipients: false }` to acknowledge the silent intent and suppress the notice — the validator counts both as explicit shapes. + ## Encrypt raw data For email addons, use `data` instead of `files`. The Thunderbird addon's crypto popup encrypts the full MIME message (body + attachments) as raw bytes, then wraps it in an email envelope: diff --git a/docs/sdk/js-errors.md b/docs/sdk/js-errors.md index 7705fc5..fa4c4e7 100644 --- a/docs/sdk/js-errors.md +++ b/docs/sdk/js-errors.md @@ -125,6 +125,7 @@ Thrown when: - The user declines disclosure in the Yivi app - The Yivi session times out - The Yivi session is aborted for any other non-success reason +- `pg.sign.yivi(...)` is called from a non-browser runtime (Node, Bun, Deno). The QR widget needs a DOM, so the call fails fast before any session starts. Use `pg.sign.apiKey(...)` or `pg.sign.session(...)` from a server runtime. Affects both `pg.encrypt(...).upload()` and `pg.encrypt(...).toBytes()` when `pg.sign.yivi(...)` is used, and `pg.open(...).decrypt(...)` when a Yivi session backs the decryption. From cc76cbba6db2bd362dfb1f640cff76abbcb5a78e Mon Sep 17 00:00:00 2001 From: "dobby-yivi-agent[bot]" <275734547+dobby-yivi-agent[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 23:02:33 +0000 Subject: [PATCH 3/3] docs: document NetworkException.Url (from encryption4all/postguard-dotnet#31) --- docs/sdk/dotnet-errors.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/sdk/dotnet-errors.md b/docs/sdk/dotnet-errors.md index 9030405..c741470 100644 --- a/docs/sdk/dotnet-errors.md +++ b/docs/sdk/dotnet-errors.md @@ -33,14 +33,17 @@ Thrown when an HTTP request to the PKG or Cryptify fails. |---|---|---| | `StatusCode` | `int` | HTTP status code | | `Body` | `string` | Response body | +| `Url` | `string` | Request URL that failed (PKG or Cryptify endpoint) | ```csharp catch (NetworkException ex) { - Console.WriteLine($"HTTP {ex.StatusCode}: {ex.Body}"); + Console.WriteLine($"HTTP {ex.StatusCode} at {ex.Url}: {ex.Body}"); } ``` +The exception message follows the same format: `HTTP {status} at {url}: {body}` (e.g. `HTTP 401 at https://pkg.postguard.eu/v2/irma/sign/key: Unauthorized`). The `Url` property lets callers wrapping this SDK distinguish a PKG failure from a Cryptify failure (and which Cryptify phase) without parsing the message. + ## `SealException` Thrown when the native cryptographic library (`libpg_ffi`) fails during encryption. This typically indicates a problem with the input data or the native library itself.