Skip to content
Draft
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
21 changes: 19 additions & 2 deletions docs/repos/cryptify.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ Configuration parameters:
| `staging_mode` | When `true`, `send_email` skips SMTP entirely and logs the intended email metadata at info level. The upload finalize still returns `Ok`. Defaults to `false`. Intended for staging deploys where real email delivery is undesirable | `false` |
| `usage_db` | Path to the SQLite database used for upload usage accounting | `/app/data/usage.db` |
| `metrics_scan_interval_secs` | Interval in seconds for the background task that samples `data_dir` size and file count for the storage gauges exposed at `GET /metrics`. Defaults to `60`. | `60` |
| `email_attribute` | Attribute type carrying the sender's email in the signing identity. Finalize requires this attribute to be present on the uploader's identity. Production keeps the default. Test environments override it with a test-scheme type. Must match the PKG's `PKG_EMAIL_ATTRIBUTE` and the SDK's `emailAttributes`. Defaults to `pbdf.sidn-pbdf.email.email`. | `irma-demo.sidn-pbdf.email.email` |

The `chunk_size` setting caps the size of each `PUT /fileupload/{uuid}` body. Clients (such as `@e4a/pg-js` and the PostGuard website) use the same value for their upload chunks, so increasing it server-side without updating the client default will not produce larger chunks on its own.

<small>[Source: src/config.rs#L3-L38](https://github.com/encryption4all/cryptify/blob/2af3ba0736ba138343295669411334af6f6de37a/src/config.rs#L3-L38)</small>
<small>[Source: src/config.rs#L3-L22](https://github.com/encryption4all/cryptify/blob/e4a618249dee43d7fe9086844dc122186b3ea07f/src/config.rs#L3-L22)</small>

### Staging mode

Expand Down Expand Up @@ -80,7 +81,7 @@ When a request would push the sender over the per-upload or the rolling-window l

`limit` is either `"per_upload"` or `"rolling_window"`. `resets_at` is an RFC 3339 timestamp for when the oldest counted upload expires from the rolling window. It is `null` for `per_upload` rejections, since the per-upload limit does not reset.

`GET /usage` returns the current state for the authenticated sender, including `used_bytes`, `limit_bytes`, `per_upload_limit_bytes`, `window_days`, and `resets_at`. When the request includes a validated `Authorization: Bearer PG-…`, the response describes the per-tenant bucket (`api-key:<tenant>`); otherwise it describes the per-email bucket.
`GET /usage` returns the current state for the caller, including `used_bytes`, `limit_bytes`, `per_upload_limit_bytes`, `window_days`, and `resets_at`. It requires a validated `Authorization: Bearer PG-…` API key and describes that key's per-tenant bucket (`api-key:<tenant>`). A missing or invalid key returns `401`; if the key cannot be confirmed because PKG is unreachable, the endpoint returns `503`. The `email` query parameter is optional and only echoed back in the response, it no longer selects which bucket is reported.

<small>[Source: src/store.rs#L11-L15](https://github.com/encryption4all/cryptify/blob/58883a86b369af08d92db93aa1025f9eba3c73eb/src/store.rs#L11-L15)</small>

Expand Down Expand Up @@ -114,8 +115,24 @@ Cryptify exposes a file upload/download API. An OpenAPI 3.0 specification is ava
- `POST /fileupload/finalize/{uuid}`: Finalize the upload (sends the recipient notification email if `notifyRecipients` was `true` on init).
- `GET /fileupload/{uuid}/status`: Read rolling-token state to resume an in-flight upload across a page refresh or tab crash. Authenticated via `X-Recovery-Token`.
- `GET /filedownload/{uuid}`: Download a file. Supports resumable downloads via the HTTP `Range` header (see [Range support on `/filedownload`](#range-support-on-filedownload) below).
- `GET /email-template`: Return the email template pg-pkg has linked to the caller's API key. Authenticated with the same `Authorization: Bearer PG-…` key as the upload endpoints (see [Email template retrieval](#email-template-retrieval) below).
- `GET /metrics`: Prometheus text-format metrics for monitoring (see [Metrics](#metrics) below). Unauthenticated; intended for scraping over a restricted network only.

### Email template retrieval

`GET /email-template` returns the email template that pg-pkg has linked to the caller's API key. The key is validated through the same `Authorization: Bearer PG-…` flow the upload endpoints use, so no separate auth path is involved.

| Case | Status | Body |
|---|---|---|
| Valid key with a template configured | `200` | `{ tenant_id, email_template }` |
| Missing or invalid key | `401` | error body |
| Valid key, no template configured | `404` | error body |
| PKG unreachable during validation | `503` | error body |

The template is resolved on the PKG side and returned as-is, keyed on the validated tenant.

<small>[Source: api-description.yaml#L434-L465](https://github.com/encryption4all/cryptify/blob/0946c066a805f1dad73c3966fb70b7aba90fea35/api-description.yaml#L434-L465)</small>

### `POST /fileupload/init` request body

| Field | Type | Required | Description |
Expand Down
15 changes: 14 additions & 1 deletion docs/repos/postguard.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,14 @@ cargo run --release --bin pg-pkg server \
|---|---|---|
| `IRMA_SERVER` | Yivi/IRMA server URL | `https://is.yivi.app` |
| `DATABASE_URL` | PostgreSQL connection string | none |
| `PKG_ALLOWED_ORIGINS` | Comma-separated CORS allowlist (e.g. `https://postguard.eu,https://postguard.nl`). `*` keeps the legacy any-origin behaviour and logs a warning at startup. Also available as `--allowed-origins`. | `*` |
| `PKG_ALLOWED_ORIGINS` | Comma-separated CORS allowlist (e.g. `https://postguard.eu,https://postguard.nl`). Required: the server refuses to start without it. Pass `*` to deliberately allow any origin (this logs a warning at startup). Also available as `--allowed-origins`. | required |
| `PKG_RATELIMIT_PER_SECOND` | Sustained request rate per client IP for the whole `/v2` scope. Also available as `--ratelimit-per-second`. | `10` |
| `PKG_RATELIMIT_BURST` | Burst allowance for the general `/v2` limit. Also available as `--ratelimit-burst`. | `50` |
| `PKG_RATELIMIT_SENSITIVE_PER_SECOND` | Sustained request rate per client IP for the key-issuing endpoints (`POST /start`, `GET /key`, `GET /key/{timestamp}`, `POST /sign/key`, `GET /api-key/validate`). Also available as `--ratelimit-sensitive-per-second`. | `2` |
| `PKG_RATELIMIT_SENSITIVE_BURST` | Burst allowance for the sensitive-endpoint limit. Also available as `--ratelimit-sensitive-burst`. | `10` |
| `PKG_RATELIMIT_DISABLED` | Set to `true` to build the limiter in permissive mode (every request passes). Use behind a trusted proxy that does its own rate limiting. Also available as `--ratelimit-disabled`. | `false` |
| `PKG_RATELIMIT_TRUST_FORWARDED_FOR` | Set to `true` to key the limiter on the rightmost `X-Forwarded-For` entry instead of the TCP peer address, so per-client limiting works behind a trusted reverse proxy. Off by default so a directly exposed PKG never trusts client-supplied headers. Also available as `--ratelimit-trust-forwarded-for`. | `false` |
| `PKG_EMAIL_ATTRIBUTE` | Attribute type carrying the email in API-key signing identities. Production keeps the default. Test environments set a test-scheme type (e.g. `irma-demo.sidn-pbdf.email.email`), since `pbdf.*` credentials cannot be issued outside production. Must match cryptify's `email_attribute` and the SDK's `emailAttributes`. Also available as `--email-attribute`. | `pbdf.sidn-pbdf.email.email` |
| `RUST_LOG` | Log level (`debug`, `info`, `warn`, `error`) | none |

### Running the PKG Server
Expand Down Expand Up @@ -255,6 +262,12 @@ Request body for signing keys:
| `GET` | `/health` | Health check. |
| `GET` | `/metrics` | Prometheus metrics. |

### Rate limiting

The `/v2` scope is rate limited per client IP in two tiers. The general tier covers the whole scope; the sensitive tier covers the key-issuing endpoints (`POST /start`, `GET /key`, `GET /key/{timestamp}`, `POST /sign/key`, `GET /api-key/validate`) with a tighter limit, applied before authentication runs. Requests over the limit get `429 Too Many Requests` with a `Retry-After` header. The limits are global across worker processes and tunable through the `PKG_RATELIMIT_*` variables listed under [Environment Variables](#environment-variables).

The limiter keys on the TCP peer address by default. When the PKG runs behind a trusted reverse proxy, set `PKG_RATELIMIT_TRUST_FORWARDED_FOR=true` so it keys on the rightmost `X-Forwarded-For` entry (the hop the trusted proxy appends) instead of the proxy's own address. Only the rightmost entry is trusted; anything left of it is client-supplied and spoofable.

### Authentication

The PKG supports two authentication methods:
Expand Down
38 changes: 38 additions & 0 deletions docs/sdk/js-encryption.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,25 @@ What gets retried: 5xx responses, fetch-level network errors (`TypeError` from `

The same `retry` config governs downloads. See [Decryption — Retries and resumable downloads](/sdk/js-decryption#retries-and-resumable-downloads).

### Email attribute types

The SDK uses the production `pbdf.sidn-pbdf.email` attribute types for recipient builders, key requests, and signing. Test setups that run against a different scheme can override these with `emailAttributes`:

```ts
const pg = new PostGuard({
pkgUrl: 'https://pkg.staging.postguard.eu',
cryptifyUrl: 'https://storage.staging.postguard.eu',
emailAttributes: {
email: 'irma-demo.sidn-pbdf.email.email',
domain: 'irma-demo.sidn-pbdf.email.domain',
},
});
```

<small>[Source: types.ts#L7-L27](https://github.com/encryption4all/postguard-js/blob/854d5c06ea7c852e8ebb8517687d71f0349bfd51/src/types.ts#L7-L27)</small>

Leave `emailAttributes` unset for production. An override must match the PKG's `PKG_EMAIL_ATTRIBUTE` and cryptify's `email_attribute`, or finalize and key issuance reject the identity.

## Resume an interrupted upload

A long-running upload can be interrupted by a page refresh, tab crash, navigation away, or process restart. The SDK exposes two primitives for rehydrating an in-flight session from Cryptify rather than starting over: the `FileState` type and the `resumeUpload` function.
Expand Down Expand Up @@ -249,6 +268,25 @@ const envelope = await pg.email.createEnvelope({

Call `.toBytes()` to get the encrypted data, or pass the `Sealed` object directly to `pg.email.createEnvelope()` for email integration.

## Prepare a signing session ahead of time

With Yivi signing, `encrypt()` starts the disclosure session after the user acts. On iOS that is a problem: a Yivi Universal Link only opens the app when the navigation happens inside a genuine user gesture, and at tap time the app deep-link does not exist yet, so the tap falls back to Safari.

`pg.prepareSign()` starts the Yivi session early. It returns `mobileUrl` (the app deep-link, resolved once Yivi shows its mobile button — put it on an `<a href>` so one tap opens the app), `keys` (resolves on disclosure), and `cancel()`. Pass the resolved keys to `encrypt()` via `signingKeys`; `encrypt()` then uses them directly and never starts a second session.

```ts
// when the compose form becomes valid:
const prep = pg.prepareSign({ element: '#hidden-yivi', attributes: SIGN_ATTRS, includeSender: true });
const href = await prep.mobileUrl; // put on the "Send" <a href>
// user taps the anchor -> Yivi app opens (one gesture)
const signingKeys = await prep.keys; // resolves after disclosure
await pg.encrypt({ files, recipients, sign, signingKeys }).upload({ notify });
```

<small>[Source: types.ts#L116-L145](https://github.com/encryption4all/postguard-js/blob/f8bd16b154b79b92bb086c2c088f10e79babb7db/src/types.ts#L116-L145)</small>

The disclosure is identity-bound (sender email plus optional attributes) and independent of the files and recipients, so the resolved keys are valid for whatever is ultimately encrypted. `sign` is still required alongside `signingKeys`: it supplies envelope metadata and the friendly-sender line when `includeSender` is set. `mobileUrl` only settles on mobile, where Yivi shows the app button; on desktop it stays pending, so race it with a timeout.

## Error handling

All encryption methods can throw:
Expand Down
Loading