Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/repos/postguard-website.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ if (uuidParam) {

<small>[Source: src/routes/(app)/decrypt/+page.svelte#L86-L106](https://github.com/encryption4all/postguard-website/blob/0398c87d113ab7b9fc518f4eb9aaf7059745d54a/src/routes/%28app%29/decrypt/%2Bpage.svelte#L86-L106)</small>

### Download confirmation gate

Since [encryption4all/postguard-website#258](https://github.com/encryption4all/postguard-website/pull/258), the `/download` page decrypts into in-memory blobs and pauses on a confirmation step before any file is written to disk. The recipient sees the file list and the verified sender, then chooses to keep or discard the files. Declining drops the blobs without writing them; accepting starts the browser downloads. When the sender disclosed nothing beyond their email, the confirmation panel warns that email alone is a weak identity claim, since anyone with control of that mailbox could have signed the message.

## Development

### Quick Start with Docker Compose (recommended)
Expand Down Expand Up @@ -123,6 +127,8 @@ adb reverse tcp:8080 tcp:8080 # PostGuard website

## Environment Variables

`VITE_*` variables are read at build time and baked into the bundle.

| Variable | Default | Description |
|---|---|---|
| `VITE_FILEHOST_URL` | `http://localhost:8000` | Cryptify file hosting service URL |
Expand All @@ -131,6 +137,20 @@ adb reverse tcp:8080 tcp:8080 # PostGuard website
| `VITE_UPLOAD_CHUNK_SIZE` | none | Upload chunk size in bytes |
| `VITE_FILEREAD_CHUNK_SIZE` | none | File read chunk size in bytes |

## Runtime config

A second tier of flags is read at page load from a global `APP_CONFIG` object served by `static/config.js`. Deployed environments overwrite the file via the Terraform ConfigMap; local Docker Compose uses the in-repo copy. Each key is optional, with a fallback baked into `src/lib/env.ts`.

| Key | Type | Default | Description |
|---|---|---|---|
| `SITE_URL` | string | `https://postguard.eu` | Base URL used by sitemap, RSS, JSON-LD, and the `(app)` routes. Marketing pages are prerendered, so the baked-in fallback is what ships in their static HTML; a runtime override only takes effect on non-prerendered routes such as `/decrypt` and `/fileshare`. Added in [encryption4all/postguard-website#255](https://github.com/encryption4all/postguard-website/pull/255). |
| `BUSINESS_URL` | string | `https://business.postguard.eu` | Link target for the "for Business" entry point. |
| `FF_BUSINESS` | boolean | `false` | Feature flag that controls whether the business entry point is shown. |
| `STAGING` | boolean | `false` | True on staging/dev where cryptify runs with `staging_mode = true` and does not send notification mail. The website renders an in-page preview of the would-be recipient email so developers can grab the download link without trawling cryptify logs. Added in [encryption4all/postguard-website#244](https://github.com/encryption4all/postguard-website/pull/244). |
| `GLITCHTIP_DSN` | string | `""` | DSN for a Sentry-compatible error reporter (PostGuard runs GlitchTip). An empty value disables reporting at module load. The fileshare flow's `CrashReport` panel uses this to POST captured exceptions on user opt-in. Added in [encryption4all/postguard-website#247](https://github.com/encryption4all/postguard-website/pull/247). |

The runtime tier exists because these values change per environment without rebuilding the static bundle. Override them by replacing `config.js` in the served container.

## Releasing

This repository uses [Release-please](https://github.com/googleapis/release-please) for automated versioning. Merging a release PR triggers a multi-architecture Docker image build pushed to GHCR.
Expand Down
17 changes: 13 additions & 4 deletions docs/sdk/js-decryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Pass `{ uuid }` to `pg.open()`, then call `decrypt()` with `element` (a CSS sele

The return type is the union `DecryptResult = DecryptFileResult | DecryptDataResult`. Which variant you get mirrors how the payload reached Cryptify:

- Uploaded via `Sealed.upload({ files })` → `DecryptFileResult` with `files`, `blob`, and a `download()` helper.
- Uploaded via `Sealed.upload({ files })` → `DecryptFileResult` with a `files` array of `{ name, blob }` entries, the raw ZIP as `blob`, and a `download()` helper that fans out one browser download per entry.
- Uploaded via `Sealed.upload({ data })` → `DecryptDataResult` with `plaintext` as a `Uint8Array`.

This keeps the round-trip `pg.encrypt({ data }).upload()` → `pg.open({ uuid }).decrypt()` symmetric: raw bytes go in, raw bytes come out. Internally, `Sealed.upload({ data })` wraps the bytes as a single-entry zip named `data.bin`; on decrypt the SDK inspects the inner zip's central directory and, when the entries are exactly `['data.bin']`, unwraps that entry and returns `DecryptDataResult`.
Expand Down Expand Up @@ -62,10 +62,14 @@ Returned when the payload was uploaded with `Sealed.upload({ files })`.

| Property | Type | Description |
|----------|------|-------------|
| `files` | `string[]` | Filenames inside the ZIP |
| `files` | `Array<{ name: string; blob: Blob }>` | One entry per file inside the ZIP, with the entry's filename and decoded `Blob` |
| `sender` | `FriendlySender \| null` | Verified sender identity with `.email` and `.attributes` |
| `blob` | `Blob` | The decrypted ZIP blob |
| `download` | `(filename?: string) => void` | Trigger a browser download |
| `blob` | `Blob` | The raw decrypted ZIP, as an escape hatch for callers that want to re-upload, hand-process, or offer a single "Download as ZIP" button |
| `download` | `() => void` | Triggers one browser download per entry in `files` |

::: warning Breaking change in v2
Prior to `@e4a/pg-js@2.0.0`, `files` was `string[]` (filenames only) and `download` took an optional `filename` argument that selected a single entry. The new `download()` takes no arguments and fans out per entry; callers that want a single combined download can pipe `blob` through their own anchor. Source: [encryption4all/postguard-js#86](https://github.com/encryption4all/postguard-js/pull/86).
:::

## Decrypt from raw data

Expand Down Expand Up @@ -105,6 +109,7 @@ Returned from `{ data }` decryption, and from `{ uuid }` when the payload was up
| `session` | `SessionCallback` | No* | Custom session callback for non-browser environments |
| `recipient` | `string` | No | Email of the recipient to decrypt for (required if multiple recipients) |
| `enableCache` | `boolean` | No | Cache the Yivi JWT so repeated decryptions don't require re-scanning the QR code |
| `onDownloadProgress` | `(pct: number \| undefined) => void` | No | Progress callback for the streaming download+decrypt pipeline. Fires on every chunk with `0–100` when the server set `Content-Length`, or `undefined` when it didn't (consumer should render an indeterminate indicator). Available since `@e4a/pg-js@2.0.0` ([#86](https://github.com/encryption4all/postguard-js/pull/86)) |

*Provide either `element` or `session`. If neither is provided, the SDK throws a `DecryptionError`.

Expand Down Expand Up @@ -160,6 +165,8 @@ The full `FriendlySender` type:
| `attributes` | `Array<{ type, value? }>` | All identity attributes |
| `raw` | `SenderIdentity` | Raw identity structure for advanced use |

Since [encryption4all/postguard-js#89](https://github.com/encryption4all/postguard-js/pull/89), `attributes` also carries the attributes the sender signed under their private signing identity (name, mobile, dateofbirth, and so on), not just the public email. Earlier versions discarded the unseal result and surfaced only the email, so recipients saw a single attribute even when the sender had disclosed more. Read the extra attributes from `attributes` to show the recipient exactly who signed the message.

## Error handling

Decryption can throw:
Expand All @@ -171,6 +178,8 @@ Decryption can throw:

Catch `IdentityMismatchError` first to show a recipient-mismatch message, then `YiviSessionError` to surface a friendly "session cancelled" message instead of a generic decryption failure, then fall through to a generic error branch for everything else.

Since `@e4a/pg-js@2.0.0`, `IdentityMismatchError` preserves the underlying error on `.cause` when the failure was not a real identity mismatch (network drop during streaming, WASM panic, malformed ciphertext). Inspect `err.cause` when debugging a transient failure that surfaces as a mismatch. An `AbortError` from a caller-supplied `signal` passes through as-is — it is not rewrapped. Source: [encryption4all/postguard-js#84](https://github.com/encryption4all/postguard-js/pull/84).

```ts
import { YiviSessionError, IdentityMismatchError } from '@e4a/pg-js';

Expand Down
9 changes: 9 additions & 0 deletions docs/sdk/js-email-helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,15 @@ const envelope = await pg.email.createEnvelope({

Callers that handle the result must null-check `envelope.attachment` before reading it, since tier 3 envelopes carry no attachment.

### Failures during upload

Tier 2 and tier 3 envelopes both attempt a Cryptify upload. Their failure semantics differ:

- **Tier 2** falls back to the local attachment when the upload rejects. The promise resolves and the envelope still contains the `postguard.encrypted` file; the failure is logged via `console.warn` so it isn't silent. The recipient can still decrypt from the attachment.
- **Tier 3** has no local fallback (the attachment is omitted by design), so the original error is re-thrown. `createEnvelope` rejects with the underlying typed error (`NetworkError`, `UploadSessionExpiredError`, or a generic `PostGuardError`) and callers should branch on the same error classes they already use around `Sealed.upload()`.

Prior to `@e4a/pg-js@2.0.0`, a tier 3 upload failure was swallowed and `createEnvelope` returned an envelope with `attachment: null` plus a body whose fallback link pointed at a non-existent Cryptify UUID. Source: [encryption4all/postguard-js#82](https://github.com/encryption4all/postguard-js/pull/82).

### Parameters

| Parameter | Type | Required | Description |
Expand Down
Loading