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.
- package/README.md +71 -0
- package/dist/bin.js +4 -20
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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",
|