pluto-rtc 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,218 @@
1
+ # iroh-wasm
2
+
3
+ WASM bindings for [iroh](https://iroh.computer) that enable P2P networking in web browsers.
4
+
5
+ This module compiles the iroh Rust library to WebAssembly, allowing browsers to participate in P2P file sharing without a backend server.
6
+
7
+ ## Architecture
8
+
9
+ ```
10
+ Browser (Web App)
11
+
12
+ WasmIrohConnection.ts (TypeScript)
13
+
14
+ WASM Module (this crate)
15
+
16
+ iroh crate (Rust networking)
17
+
18
+ WebTransport (browser API)
19
+ ```
20
+
21
+ ## Features
22
+
23
+ - **In-browser P2P**: No backend required for web clients
24
+ - **QUIC over WebTransport**: Modern protocol for efficient data transfer
25
+ - **Streaming API**: ReadableStream/WritableStream for large file transfers
26
+ - **Node persistence** (future): Use IndexedDB for persistent identity
27
+
28
+ ## Build
29
+
30
+ ### Prerequisites
31
+ - Rust toolchain
32
+ - `wasm-pack` installed: `cargo install wasm-pack`
33
+ - LLVM/Clang (for linking)
34
+
35
+ ### Build Command
36
+
37
+ From the project root:
38
+ ```bash
39
+ npm run build:wasm
40
+ ```
41
+
42
+ Or manually:
43
+ ```bash
44
+ cd src-tauri/iroh-wasm
45
+ wasm-pack build --target web --out-dir ../../src/services/iroh/wasm/pkg
46
+ ```
47
+
48
+ This generates:
49
+ - `pkg/iroh_wasm_bg.wasm` - Compiled WebAssembly binary
50
+ - `pkg/iroh_wasm.js` - JavaScript bindings
51
+ - `pkg/iroh_wasm.d.ts` - TypeScript type definitions
52
+
53
+ ### Output Location
54
+
55
+ The build outputs to `src/services/iroh/wasm/pkg/` (git-ignored) so the frontend can import it:
56
+
57
+ ```typescript
58
+ import init, { IrohNode } from './wasm/pkg/iroh_wasm';
59
+ ```
60
+
61
+ ## Source Structure
62
+
63
+ ```
64
+ src/
65
+ ├── lib.rs # WASM entry point, exports
66
+ ├── node.rs # IrohNode implementation
67
+ └── stream.rs # BiStream wrapper for QUIC streams
68
+ ```
69
+
70
+ ### lib.rs
71
+ - Initializes wasm-bindgen
72
+ - Exports `IrohNode` and `BiStream` types
73
+ - Sets up panic hooks for debugging
74
+
75
+ ### node.rs
76
+ **Type**: `IrohNode`
77
+
78
+ Main WASM-exposed class that manages the iroh node instance.
79
+
80
+ **Methods**:
81
+ ```rust
82
+ impl IrohNode {
83
+ // Create new node (in-memory for now)
84
+ #[wasm_bindgen(constructor)]
85
+ pub async fn new() -> Result<IrohNode, JsValue>;
86
+
87
+ // Get local node ID
88
+ #[wasm_bindgen]
89
+ pub fn node_id(&self) -> String;
90
+
91
+ // Connect to remote peer
92
+ #[wasm_bindgen]
93
+ pub async fn connect(&self, endpoint_addr: String)
94
+ -> Result<Connection, JsValue>;
95
+
96
+ // Accept incoming connection
97
+ #[wasm_bindgen]
98
+ pub async fn accept(&self) -> Result<Connection, JsValue>;
99
+
100
+ // Open bidirectional stream
101
+ #[wasm_bindgen]
102
+ pub async fn open_bi(&self) -> Result<BiStream, JsValue>;
103
+ }
104
+ ```
105
+
106
+ ### stream.rs
107
+ **Type**: `BiStream`
108
+
109
+ Wraps iroh's bidirectional QUIC streams in browser-compatible streams.
110
+
111
+ **Returns**:
112
+ - `ReadableStream<Uint8Array>` - For receiving data
113
+ - `WritableStream<Uint8Array>` - For sending data
114
+
115
+ **Usage from TypeScript**:
116
+ ```typescript
117
+ const stream = await connection.open_bi();
118
+ const reader = stream.readable.getReader();
119
+ const writer = stream.writable.getWriter();
120
+
121
+ // Send data
122
+ await writer.write(new Uint8Array([1, 2, 3]));
123
+
124
+ // Receive data
125
+ const { value, done } = await reader.read();
126
+ ```
127
+
128
+ ## Integration with Frontend
129
+
130
+ ### WasmIrohConnection.ts
131
+
132
+ The TypeScript wrapper (`src/services/iroh/WasmIrohConnection.ts`) uses this WASM module:
133
+
134
+ ```typescript
135
+ import init, { IrohNode } from './wasm/pkg/iroh_wasm';
136
+
137
+ // Lazy load WASM
138
+ await init();
139
+
140
+ // Create singleton node
141
+ const node = await IrohNode.new();
142
+
143
+ // Get node ID
144
+ const nodeId = node.node_id();
145
+
146
+ // Connect to peer
147
+ const connection = await node.connect(peerEndpointAddr);
148
+
149
+ // Open stream
150
+ const biStream = await connection.open_bi();
151
+ ```
152
+
153
+ ## Platform Differences
154
+
155
+ | Feature | Desktop/Mobile (Tauri) | Web (WASM) |
156
+ |---------|------------------------|------------|
157
+ | Backend | Rust via Tauri commands | Direct WASM calls |
158
+ | Storage | Persistent (filesystem) | In-memory |
159
+ | Transport | QUIC over UDP | QUIC over WebTransport |
160
+ | Performance | Native speed | Near-native |
161
+
162
+ ## Limitations
163
+
164
+ ### Current
165
+ - **No persistence**: Node identity resets on page reload
166
+ - **Memory-only**: No disk storage (by design for security)
167
+ - **Single node**: One node per browser tab
168
+
169
+ ### Future Improvements
170
+ 1. **IndexedDB storage**: Persist node identity across sessions
171
+ 2. **Service Worker**: Background P2P even when page closed
172
+ 3. **Better error handling**: More descriptive errors from Rust→JS
173
+ 4. **Connection pooling**: Reuse connections across components
174
+
175
+ ## Debugging
176
+
177
+ ### Enable WASM logs:
178
+ ```typescript
179
+ // In browser console
180
+ localStorage.setItem('debug', 'iroh:*');
181
+ ```
182
+
183
+ ### Common Issues
184
+
185
+ **"Memory access out of bounds"**
186
+ - WASM memory exhausted
187
+ - Try reducing concurrent connections/streams
188
+
189
+ **"WebTransport not supported"**
190
+ - Browser doesn't support WebTransport
191
+ - Requires Chrome 97+, Edge 97+, or Opera 83+
192
+
193
+ **"Module not found"**
194
+ - WASM not built or path incorrect
195
+ - Run `npm run build:wasm`
196
+ - Check `src/services/iroh/wasm/pkg/` exists
197
+
198
+ ## Dependencies
199
+
200
+ Key Rust crates:
201
+ - `iroh` - P2P networking
202
+ - `wasm-bindgen` - Rust↔JavaScript bindings
203
+ - `web-sys` - Browser API access
204
+ - `js-sys` - JavaScript type system
205
+
206
+ ## Performance
207
+
208
+ - **Build time**: ~60 seconds (release mode)
209
+ - **WASM size**: ~2MB (gzipped: ~600KB)
210
+ - **Startup time**: ~100ms (first load)
211
+ - **Memory**: ~5MB per node
212
+
213
+ ## See Also
214
+
215
+ - [Complete Iroh Architecture](../../docs/IROH_ARCHITECTURE.md) - Full system documentation
216
+ - [Frontend README](../../src/services/iroh/README.md) - TypeScript integration
217
+ - [Backend README](../../src-tauri/src/iroh/README.md) - Rust implementation
218
+ - [wasm-bindgen Book](https://rustwasm.github.io/wasm-bindgen/) - WASM bindings guide
@@ -0,0 +1,148 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+ /**
4
+ * The `ReadableStreamType` enum.
5
+ *
6
+ * *This API requires the following crate features to be activated: `ReadableStreamType`*
7
+ */
8
+
9
+ type ReadableStreamType = "bytes";
10
+
11
+ export class BiStream {
12
+ private constructor();
13
+ free(): void;
14
+ [Symbol.dispose](): void;
15
+ readonly endpoint_id: string;
16
+ readonly recv: ReadableStream;
17
+ readonly send: WritableStream;
18
+ }
19
+
20
+ export class IntoUnderlyingByteSource {
21
+ private constructor();
22
+ free(): void;
23
+ [Symbol.dispose](): void;
24
+ cancel(): void;
25
+ pull(controller: ReadableByteStreamController): Promise<any>;
26
+ start(controller: ReadableByteStreamController): void;
27
+ readonly autoAllocateChunkSize: number;
28
+ readonly type: ReadableStreamType;
29
+ }
30
+
31
+ export class IntoUnderlyingSink {
32
+ private constructor();
33
+ free(): void;
34
+ [Symbol.dispose](): void;
35
+ abort(reason: any): Promise<any>;
36
+ close(): Promise<any>;
37
+ write(chunk: any): Promise<any>;
38
+ }
39
+
40
+ export class IntoUnderlyingSource {
41
+ private constructor();
42
+ free(): void;
43
+ [Symbol.dispose](): void;
44
+ cancel(): void;
45
+ pull(controller: ReadableStreamDefaultController): Promise<any>;
46
+ }
47
+
48
+ export class IrohNode {
49
+ private constructor();
50
+ free(): void;
51
+ [Symbol.dispose](): void;
52
+ addNodeAddr(node_id: string, relay_url: string | null | undefined, direct_addresses: string[]): Promise<void>;
53
+ connect(endpoint_id: string): ReadableStream;
54
+ disconnect(endpoint_id: string): Promise<void>;
55
+ events(): ReadableStream;
56
+ incoming_streams(): ReadableStream;
57
+ node_addr(): Promise<string>;
58
+ open_bi(endpoint_id: string): Promise<BiStream>;
59
+ open_uni(endpoint_id: string): Promise<WritableStream>;
60
+ static spawn(secret_key?: Uint8Array | null): Promise<IrohNode>;
61
+ readonly endpoint_id: string;
62
+ readonly secret_key: Uint8Array;
63
+ }
64
+
65
+ export function start(): void;
66
+
67
+ export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
68
+
69
+ export interface InitOutput {
70
+ readonly memory: WebAssembly.Memory;
71
+ readonly start: () => void;
72
+ readonly __wbg_bistream_free: (a: number, b: number) => void;
73
+ readonly bistream_recv: (a: number) => any;
74
+ readonly bistream_send: (a: number) => any;
75
+ readonly bistream_endpoint_id: (a: number) => [number, number];
76
+ readonly __wbg_irohnode_free: (a: number, b: number) => void;
77
+ readonly irohnode_spawn: (a: number, b: number) => any;
78
+ readonly irohnode_events: (a: number) => any;
79
+ readonly irohnode_incoming_streams: (a: number) => any;
80
+ readonly irohnode_addNodeAddr: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => any;
81
+ readonly irohnode_endpoint_id: (a: number) => [number, number];
82
+ readonly irohnode_secret_key: (a: number) => [number, number];
83
+ readonly irohnode_node_addr: (a: number) => any;
84
+ readonly irohnode_connect: (a: number, b: number, c: number) => [number, number, number];
85
+ readonly irohnode_disconnect: (a: number, b: number, c: number) => any;
86
+ readonly irohnode_open_bi: (a: number, b: number, c: number) => any;
87
+ readonly irohnode_open_uni: (a: number, b: number, c: number) => any;
88
+ readonly __wbg_intounderlyingsource_free: (a: number, b: number) => void;
89
+ readonly intounderlyingsource_pull: (a: number, b: any) => any;
90
+ readonly intounderlyingsource_cancel: (a: number) => void;
91
+ readonly __wbg_intounderlyingsink_free: (a: number, b: number) => void;
92
+ readonly intounderlyingsink_write: (a: number, b: any) => any;
93
+ readonly intounderlyingsink_close: (a: number) => any;
94
+ readonly intounderlyingsink_abort: (a: number, b: any) => any;
95
+ readonly __wbg_intounderlyingbytesource_free: (a: number, b: number) => void;
96
+ readonly intounderlyingbytesource_type: (a: number) => number;
97
+ readonly intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number;
98
+ readonly intounderlyingbytesource_start: (a: number, b: any) => void;
99
+ readonly intounderlyingbytesource_pull: (a: number, b: any) => any;
100
+ readonly intounderlyingbytesource_cancel: (a: number) => void;
101
+ readonly ring_core_0_17_14__bn_mul_mont: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
102
+ readonly wasm_bindgen__closure__destroy__h7cf29a7e95a6517c: (a: number, b: number) => void;
103
+ readonly wasm_bindgen__closure__destroy__heb08e36b77a85b9c: (a: number, b: number) => void;
104
+ readonly wasm_bindgen__closure__destroy__h18332ba013e398aa: (a: number, b: number) => void;
105
+ readonly wasm_bindgen__closure__destroy__h87cf50f4bd51ca2e: (a: number, b: number) => void;
106
+ readonly wasm_bindgen__closure__destroy__h920903f13ccee978: (a: number, b: number) => void;
107
+ readonly wasm_bindgen__closure__destroy__h5ecb98b48ddb9e08: (a: number, b: number) => void;
108
+ readonly wasm_bindgen__closure__destroy__h5c4e7138d6491581: (a: number, b: number) => void;
109
+ readonly wasm_bindgen__convert__closures_____invoke__h39b19e5fabe12541: (a: number, b: number, c: any, d: any) => void;
110
+ readonly wasm_bindgen__convert__closures_____invoke__h993a01742f0967bf: (a: number, b: number, c: any) => void;
111
+ readonly wasm_bindgen__convert__closures_____invoke__hb5204d4e41ff3977: (a: number, b: number, c: any) => void;
112
+ readonly wasm_bindgen__convert__closures_____invoke__h650bb17850b2b136: (a: number, b: number, c: any) => void;
113
+ readonly wasm_bindgen__convert__closures_____invoke__h36c5adc97d437817: (a: number, b: number) => void;
114
+ readonly wasm_bindgen__convert__closures_____invoke__hb17976c66e1a6d68: (a: number, b: number) => void;
115
+ readonly wasm_bindgen__convert__closures_____invoke__h81b4c013358ef500: (a: number, b: number) => void;
116
+ readonly wasm_bindgen__convert__closures_____invoke__h19f413c72615257c: (a: number, b: number) => void;
117
+ readonly __wbindgen_malloc: (a: number, b: number) => number;
118
+ readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
119
+ readonly __wbindgen_exn_store: (a: number) => void;
120
+ readonly __externref_table_alloc: () => number;
121
+ readonly __wbindgen_externrefs: WebAssembly.Table;
122
+ readonly __externref_drop_slice: (a: number, b: number) => void;
123
+ readonly __wbindgen_free: (a: number, b: number, c: number) => void;
124
+ readonly __externref_table_dealloc: (a: number) => void;
125
+ readonly __wbindgen_start: () => void;
126
+ }
127
+
128
+ export type SyncInitInput = BufferSource | WebAssembly.Module;
129
+
130
+ /**
131
+ * Instantiates the given `module`, which can either be bytes or
132
+ * a precompiled `WebAssembly.Module`.
133
+ *
134
+ * @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
135
+ *
136
+ * @returns {InitOutput}
137
+ */
138
+ export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
139
+
140
+ /**
141
+ * If `module_or_path` is {RequestInfo} or {URL}, makes a request and
142
+ * for everything else, calls `WebAssembly.instantiate` directly.
143
+ *
144
+ * @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
145
+ *
146
+ * @returns {Promise<InitOutput>}
147
+ */
148
+ export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;