fied 0.2.2 → 0.2.4

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.
Files changed (3) hide show
  1. package/README.md +71 -0
  2. package/dist/bin.js +4 -20
  3. package/package.json +3 -2
package/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # fied
2
+
3
+ Share your tmux session in the browser. End-to-end encrypted.
4
+
5
+ ```
6
+ npx fied
7
+ ```
8
+
9
+ You get a link. Anyone with the link sees your terminal in real time and can type into it. The server never sees your data — the encryption key lives in the URL fragment (`#`), which never leaves the browser.
10
+
11
+ ## How it works
12
+
13
+ ```
14
+ tmux on your machine
15
+ ↕ AES-256-GCM encrypted WebSocket
16
+ fied.app relay (sees only opaque bytes)
17
+ ↕ AES-256-GCM encrypted WebSocket
18
+ Browser viewer (decrypts with key from URL #fragment)
19
+ ```
20
+
21
+ 1. `npx fied` attaches to your tmux session and generates a 256-bit AES key
22
+ 2. You get a URL like `https://fied.app/s/a1b2c3d4#<key>`
23
+ 3. The viewer opens the link, decrypts in-browser, renders via xterm.js
24
+ 4. Keystrokes travel back the same encrypted path — fully interactive
25
+
26
+ ## Usage
27
+
28
+ ```bash
29
+ # Share the only tmux session (auto-detected)
30
+ npx fied
31
+
32
+ # Share a specific session
33
+ npx fied -s mysession
34
+
35
+ # Use a custom relay (self-hosted)
36
+ npx fied --relay https://your-relay.com
37
+ ```
38
+
39
+ Running `npx fied` with no active share session opens a picker to select a tmux session. After the link is shown, you can send it to the background and manage active shares by running `npx fied` again.
40
+
41
+ ## Requirements
42
+
43
+ - Node.js 18+
44
+ - A running tmux session
45
+
46
+ ## Features
47
+
48
+ - **End-to-end encrypted** — AES-256-GCM, key never reaches the server
49
+ - **Interactive** — viewers can type, not just watch
50
+ - **Background mode** — share persists after closing the terminal
51
+ - **Session management** — list, stop, or reconnect to active shares
52
+ - **Mobile support** — works on phones and tablets
53
+ - **Up to 5 concurrent viewers** per session
54
+ - **Auto-reconnect** — survives brief network interruptions
55
+ - **Zero config** — just `npx fied`
56
+
57
+ ## Security
58
+
59
+ - AES-256-GCM with random IV per message
60
+ - Key placed in URL fragment — [never sent to the server](https://datatracker.ietf.org/doc/html/rfc3986#section-3.5)
61
+ - Relay forwards opaque binary blobs
62
+ - All traffic over WSS (TLS)
63
+ - Rate-limited session creation
64
+
65
+ ## Self-hosting
66
+
67
+ The relay is a Cloudflare Worker. See the [repo](https://github.com/gstohl/fied) for deployment instructions.
68
+
69
+ ## License
70
+
71
+ MIT
package/dist/bin.js CHANGED
@@ -231,7 +231,6 @@ async function share(options) {
231
231
  console.log("");
232
232
  }
233
233
  const bridge = new RelayBridge(relayTarget, cryptoKey, keyFragment, pty, options.background, options.sessionId);
234
- let handoffRequested = false;
235
234
  const onUrl = (url) => {
236
235
  if (options.background) {
237
236
  addSession({
@@ -242,24 +241,9 @@ async function share(options) {
242
241
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
243
242
  });
244
243
  }
245
- if (options.onShareUrl) {
246
- return Promise.resolve(options.onShareUrl(url)).then((action) => {
247
- if (action === "handoff") {
248
- handoffRequested = true;
249
- }
250
- });
251
- }
252
- return Promise.resolve();
244
+ return options.onShareUrl?.(url);
253
245
  };
254
246
  await bridge.connect(onUrl);
255
- if (handoffRequested) {
256
- bridge.destroy();
257
- try {
258
- pty.kill();
259
- } catch {
260
- }
261
- return;
262
- }
263
247
  let closed = false;
264
248
  const cleanup = () => {
265
249
  if (closed) return;
@@ -370,7 +354,7 @@ var RelayBridge = class {
370
354
  console.log(" \x1B[2mPress Ctrl+C to stop sharing.\x1B[0m");
371
355
  console.log("");
372
356
  }
373
- await this.onUrl?.(url);
357
+ void this.onUrl?.(url);
374
358
  }
375
359
  const wsUrl = new URL(`api/sessions/${this.sessionId}/ws`, this.relayTarget.wsBase);
376
360
  wsUrl.searchParams.set("role", "host");
@@ -669,7 +653,7 @@ async function main() {
669
653
  onShareUrl: async (url) => {
670
654
  const background = await confirm("Run in background?");
671
655
  if (!background) {
672
- return "continue";
656
+ return;
673
657
  }
674
658
  const parsed = parseShareUrl(url);
675
659
  const child = spawnBackground({
@@ -685,7 +669,7 @@ async function main() {
685
669
  console.error(" Same share link stays active.");
686
670
  console.error(" Run \x1B[1mnpx fied\x1B[0m again to manage.");
687
671
  console.error("");
688
- return "handoff";
672
+ setTimeout(() => process.exit(0), 300);
689
673
  }
690
674
  });
691
675
  }
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "fied",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Share your tmux session in the browser with end-to-end encryption",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "fied": "./dist/bin.js"
8
8
  },
9
9
  "files": [
10
- "dist/bin.js"
10
+ "dist/bin.js",
11
+ "README.md"
11
12
  ],
12
13
  "scripts": {
13
14
  "build": "node build.mjs",