Cryptify shipped the upload-resume protocol (cryptify#148, cryptify#145, cryptify#144) as part of cryptify#136. pg-js already covers some of it but the cross-restart path is missing.
What pg-js already does
- `storeChunkWithRetry` sends `prevToken` on retry → exercises cryptify's idempotent-retry path (#145).
- `throwSessionExpiredOrNetworkError` parses the structured 404 body and surfaces `UploadSessionExpiredError` → uses cryptify#144.
- In-process retries on a flaky connection work today.
What is missing
1. `recovery_token` is not captured from `initUpload`
`POST /fileupload/init` now returns a 32-byte hex `recovery_token` in the JSON body. `initUpload` in `src/api/cryptify.ts` only reads `uuid` from the JSON and the rolling token from the `cryptifytoken` header. The recovery token is silently dropped.
It needs to be returned from `initUpload` so the caller can persist it. Suggested shape:
```ts
interface FileState {
token: string;
uuid: string;
recoveryToken: string; // new — required for cross-restart resume
prevToken?: string;
}
```
2. No `GET /fileupload/{uuid}/status` call
When an addon/website is resumed after a refresh or process restart, it has `{uuid, recoveryToken}` from local storage but no `cryptify_token`. There's currently no pg-js function that calls `GET /fileupload/{uuid}/status` with `X-Recovery-Token` to rehydrate `FileState`.
Suggested API:
```ts
export async function resumeUpload(
cryptifyUrl: string,
uuid: string,
recoveryToken: string,
signal?: AbortSignal
): Promise<{ state: FileState; uploaded: number }>;
```
- Returns the current `uploaded` byte offset and the rehydrated `FileState` (token, prevToken if present).
- 404 `upload_session_not_found` → `UploadSessionExpiredError` (reuse the existing throw helper).
3. Persistence is consumer-owned
pg-js shouldn't try to persist `{uuid, recoveryToken}` itself — the storage layer differs per host (Office addin roamingSettings, Thunderbird `browser.storage`, website IndexedDB). Just expose the values and let the consumer decide. The consumer issues are tracked downstream:
Related — separate issue
Download-side Range support has its own bug: pg-js's `downloadRange` already requires `206 Partial Content`, but cryptify's `FileServer` never returns 206 (it uses Rocket's `sized_body`, which has no Range handling). That's tracked at cryptify#153 — server-side fix, no pg-js change needed once cryptify honours Range.
Cryptify shipped the upload-resume protocol (cryptify#148, cryptify#145, cryptify#144) as part of cryptify#136. pg-js already covers some of it but the cross-restart path is missing.
What pg-js already does
What is missing
1. `recovery_token` is not captured from `initUpload`
`POST /fileupload/init` now returns a 32-byte hex `recovery_token` in the JSON body. `initUpload` in `src/api/cryptify.ts` only reads `uuid` from the JSON and the rolling token from the `cryptifytoken` header. The recovery token is silently dropped.
It needs to be returned from `initUpload` so the caller can persist it. Suggested shape:
```ts
interface FileState {
token: string;
uuid: string;
recoveryToken: string; // new — required for cross-restart resume
prevToken?: string;
}
```
2. No `GET /fileupload/{uuid}/status` call
When an addon/website is resumed after a refresh or process restart, it has `{uuid, recoveryToken}` from local storage but no `cryptify_token`. There's currently no pg-js function that calls `GET /fileupload/{uuid}/status` with `X-Recovery-Token` to rehydrate `FileState`.
Suggested API:
```ts
export async function resumeUpload(
cryptifyUrl: string,
uuid: string,
recoveryToken: string,
signal?: AbortSignal
): Promise<{ state: FileState; uploaded: number }>;
```
3. Persistence is consumer-owned
pg-js shouldn't try to persist `{uuid, recoveryToken}` itself — the storage layer differs per host (Office addin roamingSettings, Thunderbird `browser.storage`, website IndexedDB). Just expose the values and let the consumer decide. The consumer issues are tracked downstream:
Related — separate issue
Download-side Range support has its own bug: pg-js's `downloadRange` already requires `206 Partial Content`, but cryptify's `FileServer` never returns 206 (it uses Rocket's `sized_body`, which has no Range handling). That's tracked at cryptify#153 — server-side fix, no pg-js change needed once cryptify honours Range.