Skip to content

Commit 62c3c7b

Browse files
committed
feat(upload): silent-by-default, opt-in notify.recipients/notify.sender
Closes #40. Today `sealed.upload` always triggered Cryptify's per-recipient notification email — there was no client-side opt-out. The existing `notify` block only customized mail content; the docstring even claimed `notify` *enabled* the recipient mail, which was the opposite of reality. That made the upload pipeline unsuitable for any caller delivering the encrypted payload through another channel (e.g. an email add-in delivering the message from the user's own mailbox), where the Cryptify mail arrived as a duplicate. Redesigns `UploadOptions['notify']` to a symmetric, silent-by-default shape: ```ts sealed.upload({ notify: { recipients: true, // opt-in (default false) sender: true, // opt-in (default false; was confirmToSender) message: 'See you...', // unencrypted body for any mails sent language: 'EN', }, }); ``` `pg.email.createEnvelope` forwards `notify` to the underlying upload for tier 2/3 envelopes. The SDK now always sends the wire-level `notifyRecipients` field with explicit `false` when omitted, so older Cryptify deployments (which default `notifyRecipients` to true) get overridden to the SDK's silent-by-default semantics. Coordinated server-side change: encryption4all/cryptify#135. BREAKING: `notify.confirmToSender` renamed to `notify.sender` for symmetry with `notify.recipients`. Per upstream guidance there are no production consumers of `@e4a/pg-js` yet, so we ship the cleaner name rather than alias the old one. Downstream consumers (the website fileshare, the sveltekit example) need to switch from `confirmToSender: true` to `recipients: true, sender: true` to keep emitting both mails — those updates land separately. Also fixes the misleading `Sealed.upload` docstring.
1 parent aad433b commit 62c3c7b

5 files changed

Lines changed: 48 additions & 7 deletions

File tree

src/api/cryptify.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@ export interface InitUploadOptions {
99
recipient: string;
1010
mailContent?: string;
1111
mailLang?: 'EN' | 'NL';
12+
/** Send a confirmation email to the sender. Default false. Maps to
13+
* the wire-level `confirm` field. */
1214
confirm?: boolean;
15+
/** Send a notification email to each recipient. Default false in the
16+
* SDK (overrides Cryptify's server-side default of true for clients
17+
* that don't send the field, so callers get a silent upload by
18+
* default). Requires cryptify ≥ the release that added the
19+
* `notifyRecipients` field; older servers ignore it and continue to
20+
* email recipients. */
21+
notifyRecipients?: boolean;
1322
signal?: AbortSignal;
1423
}
1524

@@ -27,6 +36,10 @@ export async function initUpload(
2736
recipient: options.recipient,
2837
mailContent: options.mailContent ?? '',
2938
mailLang: options.mailLang ?? 'EN',
39+
// Always send the field so older Cryptify deployments (default
40+
// notifyRecipients: true) get explicitly overridden to the SDK's
41+
// silent-by-default semantics.
42+
notifyRecipients: options.notifyRecipients ?? false,
3043
}),
3144
});
3245

src/crypto/encrypt.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ export interface EncryptPipelineOptions {
2121
/** Size (in bytes) of each chunk sent during upload. Defaults to 5 000 000 (5 MB). */
2222
uploadChunkSize?: number;
2323
delivery?: {
24+
/** Send a notification email to each recipient. Default false. */
25+
recipients?: boolean;
26+
/** Send a confirmation email to the sender. Default false. */
27+
sender?: boolean;
2428
message?: string;
2529
language?: 'EN' | 'NL';
26-
confirmToSender?: boolean;
2730
};
2831
headers?: HeadersInit;
2932
/** Pre-resolved signing keys (skips Yivi/API key resolution if provided) */
@@ -80,7 +83,8 @@ export async function encryptPipeline(options: EncryptPipelineOptions): Promise<
8083
recipient: recipientEmails,
8184
mailContent: delivery?.message,
8285
mailLang: delivery?.language,
83-
confirm: delivery?.confirmToSender,
86+
confirm: delivery?.sender,
87+
notifyRecipients: delivery?.recipients,
8488
abortSignal: effectiveSignal,
8589
onProgress: (uploaded, last) => {
8690
if (onProgress) {

src/email/envelope.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const DEFAULT_WEBSITE_URL = 'https://postguard.eu';
3333
* Outlook's 1 M-char setAsync limit and was redundant with the
3434
* attachment / fragment link. */
3535
export async function createEnvelope(options: CreateEnvelopeOptions): Promise<EnvelopeResult> {
36-
const { sealed, from, unencryptedMessage, senderAttributes } = options;
36+
const { sealed, from, unencryptedMessage, senderAttributes, notify } = options;
3737
const websiteUrl = options.websiteUrl ?? DEFAULT_WEBSITE_URL;
3838
const uploadToCryptify = options.uploadToCryptify ?? true;
3939
const logoUrl = `${websiteUrl}/pg_logo.png`;
@@ -59,7 +59,7 @@ export async function createEnvelope(options: CreateEnvelopeOptions): Promise<En
5959

6060
if (tryUpload) {
6161
try {
62-
const result = await sealed.upload();
62+
const result = await sealed.upload(notify ? { notify } : undefined);
6363
uploadUuid = result.uuid;
6464
} catch {
6565
// Network / CORS / Cryptify-unavailable. Fall through to

src/sealed.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ export class Sealed {
7171
}
7272

7373
/** Encrypt and upload to Cryptify (streams internally for efficiency).
74-
* Pass `notify` to have Cryptify send email notifications to recipients. */
74+
* Silent by default — pass `notify.recipients = true` to have
75+
* Cryptify email each recipient a download link, and/or
76+
* `notify.sender = true` for a confirmation back to the sender.
77+
* `notify.message` adds an optional unencrypted body shared by both
78+
* mails. */
7579
async upload(opts?: UploadOptions): Promise<UploadResult> {
7680
if (!this.config.cryptifyUrl) {
7781
throw new Error('cryptifyUrl is required for upload');

src/types.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,25 @@ export interface EncryptInput {
7676

7777
/** Options for sealed.upload() */
7878
export interface UploadOptions {
79-
/** If provided, Cryptify sends email notifications to recipients */
79+
/** Cryptify notification settings. Both recipient and sender mails
80+
* are opt-in: omit `notify` (or omit the `recipients` / `sender`
81+
* fields) and the upload is silent. Use this when the encrypted
82+
* payload is being delivered through another channel (e.g. an email
83+
* client) — pass an explicit toggle when Cryptify itself should
84+
* email anyone. */
8085
notify?: {
86+
/** Send a notification email to each recipient with a download
87+
* link. Default false. */
88+
recipients?: boolean;
89+
/** Send a confirmation email back to the sender. Default false.
90+
* Independent of `recipients`. */
91+
sender?: boolean;
92+
/** Optional unencrypted message body included in any notification
93+
* email(s) sent — both the per-recipient mail and the sender
94+
* confirmation, when those are enabled. */
8195
message?: string;
96+
/** Notification email template language. Default 'EN'. */
8297
language?: 'EN' | 'NL';
83-
confirmToSender?: boolean;
8498
};
8599
}
86100

@@ -208,6 +222,12 @@ export interface CreateEnvelopeOptions {
208222
* Tier 2; Tier 3 (over `PG_MAX_ATTACHMENT_SIZE`) always uploads because
209223
* there is no attachment fallback. */
210224
uploadToCryptify?: boolean;
225+
/** Notification settings for the underlying Cryptify upload. Same
226+
* shape as `Sealed.upload`'s `notify`. Silent by default — set
227+
* `notify.recipients = true` to opt into per-recipient mails, etc.
228+
* Has no effect on Tier 1 (no upload happens) or when
229+
* `uploadToCryptify: false` already skipped the upload. */
230+
notify?: UploadOptions['notify'];
211231
}
212232

213233
/** Which tier the envelope falls into based on encrypted payload size.

0 commit comments

Comments
 (0)