Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9f67e75
prepare xftp-web for npm publishing
shumvgolove Feb 18, 2026
c85050c
fix public API: sodium init, error exports, missing types
shumvgolove Feb 18, 2026
eaa81dc
add README with usage and development instructions
shumvgolove Feb 18, 2026
ca2781a
distribute upload chunks across multiple servers
shumvgolove Feb 18, 2026
17995b3
implement FACK protocol command and ackXFTPChunk
shumvgolove Feb 18, 2026
122d039
parallel upload, FACK on download, multi-recipient, auth passthrough
shumvgolove Feb 18, 2026
9c131e1
parse BLOCKED error into structured BlockingInfo
shumvgolove Feb 18, 2026
bc0f5e0
FADD batching, parallel delete and redirect download
shumvgolove Feb 18, 2026
e977892
update README for multi-server uploads and upload options
shumvgolove Feb 18, 2026
9e64919
add MAX_RECIPIENTS_PER_REQUEST comment
shumvgolove Feb 18, 2026
995944c
async non-blocking encryptFileForUpload with 64KB yielding
shumvgolove Feb 18, 2026
52f9c00
add async/sync encryption equivalence tests
shumvgolove Feb 18, 2026
2647a5b
streaming encryption API: onSlice callback for memory-efficient large…
shumvgolove Feb 19, 2026
eb759ee
extract uploadSingleChunk helper, deduplicate uploadFile and uploadRe…
shumvgolove Feb 19, 2026
8519a74
streaming sendFile pipeline, receiveFile, prepareEncryption helper
shumvgolove Feb 19, 2026
4df6a2c
convert XFTPClientAgent interface to XFTPAgent class
shumvgolove Feb 19, 2026
7d69d9b
remove dead exports, add XFTPAgent.closeServer method
shumvgolove Feb 19, 2026
15ef669
README: add receiveFile progress, clarify deleteFile takes sndDescrip…
shumvgolove Feb 19, 2026
7fdc8ea
fix worker message race: wait for ready signal before posting messages
shumvgolove Feb 19, 2026
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
133 changes: 118 additions & 15 deletions xftp-web/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,116 @@

Browser-compatible XFTP file transfer client in TypeScript.

## Prerequisites
## Installation

```bash
npm install xftp-web
```

## Usage

```typescript
import {
XFTPAgent,
parseXFTPServer,
sendFile, receiveFile, deleteFile,
XFTPRetriableError, XFTPPermanentError, isRetriable,
} from "xftp-web"

// Create agent (manages connections)
const agent = new XFTPAgent()

const servers = [
parseXFTPServer("xftp://server1..."),
parseXFTPServer("xftp://server2..."),
parseXFTPServer("xftp://server3..."),
]

// Upload (from Uint8Array)
const {rcvDescriptions, sndDescription, uri} = await sendFile(
agent, servers, fileBytes, "photo.jpg",
{onProgress: (uploaded, total) => console.log(`${uploaded}/${total}`)}
)

// Upload (streaming — constant memory, no full-file buffer)
const file = inputEl.files[0]
const result = await sendFile(
agent, servers, file.stream(), file.size, file.name,
{onProgress: (uploaded, total) => console.log(`${uploaded}/${total}`)}
)

// Download
const {header, content} = await receiveFile(agent, uri, {
onProgress: (downloaded, total) => console.log(`${downloaded}/${total}`)
})

// Delete (requires sender description from upload)
await deleteFile(agent, sndDescription)

// Cleanup
agent.close()
```

### Advanced usage

For streaming encryption (avoids buffering the full encrypted file) or worker-based uploads:

```typescript
import {
encryptFileForUpload, uploadFile, downloadFile,
decodeDescriptionURI,
} from "xftp-web"

// Streaming encryption — encrypted slices emitted via callback
const metadata = await encryptFileForUpload(fileBytes, "photo.jpg", {
onSlice: (data) => { /* write to OPFS, IndexedDB, etc. */ },
onProgress: (done, total) => {},
})
// metadata has {digest, key, nonce, chunkSizes} but no encData

// Upload with custom chunk reader (e.g. reading from OPFS)
const result = await uploadFile(agent, servers, metadata, {
readChunk: (offset, size) => readFromStorage(offset, size),
})

// Download with FileDescription object
const fd = decodeDescriptionURI(uri)
const {header, content} = await downloadFile(agent, fd)
```

### Upload options

```typescript
await sendFile(agent, servers, fileBytes, "photo.jpg", {
onProgress: (uploaded, total) => {}, // progress callback
auth: basicAuthBytes, // BasicAuth for auth-required servers
numRecipients: 3, // multiple independent download credentials (default: 1)
})
```

### Error handling

```typescript
try {
await sendFile(agent, servers, fileBytes, "photo.jpg")
} catch (e) {
if (e instanceof XFTPRetriableError) {
// Network/timeout/session errors — safe to retry
} else if (e instanceof XFTPPermanentError) {
// AUTH, NO_FILE, BLOCKED, etc. — do not retry
}
// or use: isRetriable(e)
}
```

## Development

### Prerequisites

- Haskell toolchain with `cabal` (to build `xftp-server`)
- Node.js 20+
- Chromium system dependencies (see below)

## Setup
### Setup

```bash
# Build the XFTP server binary (from repo root)
Expand All @@ -17,31 +120,31 @@ cabal build xftp-server
# Install JS dependencies
cd xftp-web
npm install

# Install Chromium for Playwright (browser tests)
npx playwright install chromium
```

If Chromium fails to launch due to missing system libraries, install them with:
### Running tests

```bash
# Requires root
npx playwright install-deps chromium
npm run test
```

## Running tests
The `pretest` script automatically installs Chromium and sets up the libsodium symlink. The browser test starts an `xftp-server` instance on port 7000 via `globalSetup`.

If Chromium fails to launch due to missing system libraries:

```bash
# Browser round-trip test (vitest + Playwright headless Chromium)
npm run test
# Requires root
npx playwright install-deps chromium
```

The browser test automatically starts an `xftp-server` instance on port 7000 via `globalSetup`, using certs from `tests/fixtures/`.

## Build
### Build

```bash
npm run build
```

Output goes to `dist/`.

## License

[AGPL-3.0-only](https://www.gnu.org/licenses/agpl-3.0.html)
15 changes: 13 additions & 2 deletions xftp-web/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
{
"name": "xftp-web",
"version": "0.1.0",
"private": true,
"description": "XFTP file transfer protocol client for web/browser environments",
"license": "AGPL-3.0-only",
"repository": {
"type": "git",
"url": "https://github.com/simplex-chat/simplexmq",
"directory": "xftp-web"
},
"type": "module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": ["dist", "src"],
"publishConfig": {
"access": "public"
},
"scripts": {
"postinstall": "ln -sf ../../../libsodium-sumo/dist/modules-sumo-esm/libsodium-sumo.mjs node_modules/libsodium-wrappers-sumo/dist/modules-sumo-esm/libsodium-sumo.mjs && npx playwright install chromium",
"prepublishOnly": "npm run build",
"pretest": "ln -sf ../../../libsodium-sumo/dist/modules-sumo-esm/libsodium-sumo.mjs node_modules/libsodium-wrappers-sumo/dist/modules-sumo-esm/libsodium-sumo.mjs && npx playwright install chromium",
"build": "tsc",
"test": "vitest",
"dev": "npx tsx test/runSetup.ts && vite --mode development",
Expand Down
Loading
Loading