agent-yes 1.276.0 → 1.277.1
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 +10 -9
- package/agent-yes.config.schema.json +4 -1
- package/default.config.yaml +49 -0
- package/dist/{SUPPORTED_CLIS-G5YhpzKV.js → SUPPORTED_CLIS-DMA2T-LF.js} +2 -2
- package/dist/{SUPPORTED_CLIS-D-hylNcp.js → SUPPORTED_CLIS-DzVEFVKI.js} +3 -3
- package/dist/{agentShare-BmQZ96CJ.js → agentShare-DqT9USG5.js} +3 -3
- package/dist/{callback-BEgUEMY3.js → callback-D60U9zFd.js} +3 -3
- package/dist/{callback-B2oH6mCo.js → callback-PKIAkhNq.js} +3 -3
- package/dist/cli.js +11 -11
- package/dist/cmdDsh-BySPA9hP.js +45 -0
- package/dist/{cmdPs-E6fcHruY.js → cmdPs-BeUkjSOH.js} +3 -3
- package/dist/{cwdConflictWarn-CqN5pJ-7.js → cwdConflictWarn-DIPD6HH5.js} +1 -1
- package/dist/{cwdPassthroughHint-DxdqgwEN.js → cwdPassthroughHint-C0tfZlgZ.js} +1 -1
- package/dist/dsh-legacy-yes.js +10 -0
- package/dist/dsh-tui-yes.js +10 -0
- package/dist/dsh-yes.js +10 -0
- package/dist/{expose-DTxYWQeW.js → expose-Ds9aUFsq.js} +1 -1
- package/dist/{forkNested-CZAvQj0y.js → forkNested-Dt5XP8ZH.js} +1 -1
- package/dist/{hist-QkZZBp-y.js → hist-DSYaJEd3.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/{initMsg-DAwRS89d.js → initMsg-CeF4QNOM.js} +1 -1
- package/dist/{initMsg-C8ISz_C7.js → initMsg-DejbYYjX.js} +1 -1
- package/dist/{nodeRuntime-C_LtZrpR.js → nodeRuntime-_zeWDISM.js} +1 -1
- package/dist/{notifyDaemon-D_x5ow8y.js → notifyDaemon-Bw70QCkC.js} +3 -3
- package/dist/{openBrowser-B50Lhaob.js → openBrowser-BNP8xByu.js} +1 -1
- package/dist/{parentLink-svMgYhEi.js → parentLink-Bv8ihRcu.js} +1 -1
- package/dist/{parentLink-i817PzXK.js → parentLink-HWunrH1s.js} +1 -1
- package/dist/{parentPingLoop-DP2y906e.js → parentPingLoop-DJAPbp6D.js} +4 -4
- package/dist/{remotes-gAI6-nv9.js → remotes-BlHTR9ow.js} +1 -1
- package/dist/{remotes-Cdlaj7AP.js → remotes-b6reVXzo.js} +2 -2
- package/dist/{rustBinary-BwgA8XyI.js → rustBinary-CxtSgRSg.js} +2 -2
- package/dist/{rustBinary-bXjmCsim.js → rustBinary-DQHcq1Wr.js} +2 -2
- package/dist/{schedule-CdEUIaEV.js → schedule-BI7-Pa2F.js} +6 -6
- package/dist/{serve-CvLw3YZx.js → serve-Bfxc3VWQ.js} +26 -26
- package/dist/{setup-BRyFMtpv.js → setup-Cp31FALW.js} +4 -4
- package/dist/{spawnGate-DtC_ffme.js → spawnGate-B2hA_2NQ.js} +2 -2
- package/dist/{spawnGate-CdewnL8m.js → spawnGate-FLwTAPW6.js} +1 -1
- package/dist/{subcommands-CqBONdKo.js → subcommands-CjgHXjZd.js} +2 -2
- package/dist/{subcommands-BtRA2slB.js → subcommands-Cohq91Dx.js} +27 -22
- package/dist/{systemPathLink-Df_pstm2.js → systemPathLink-Dl2gJqeg.js} +1 -1
- package/dist/{terminal-8jfLRLtF.js → terminal-8ipst6Wf.js} +2 -2
- package/dist/{todoCli-Cf0EHvfy.js → todoCli-MWrejmdM.js} +1 -1
- package/dist/{tray-BeIs4ffu.js → tray-BhPVpP_r.js} +1 -1
- package/dist/{trayApp-CnJiwYjb.js → trayApp-CL_iZpnR.js} +2 -2
- package/dist/trayApp-DaekNsVe.js +5 -0
- package/dist/{ts-CEtUijwR.js → ts-RBN1OMQ_.js} +5 -5
- package/dist/{versionChecker-CL6Dvn--.js → versionChecker-F_WiFsVg.js} +2 -2
- package/dist/{webrtcRemote-CIbq5Hdt.js → webrtcRemote-H9Hok0_R.js} +1 -1
- package/dist/{widget-DvSx4hyW.js → widget-DTramHv8.js} +3 -3
- package/dist/{workspaceConfig-DctWuxUP.js → workspaceConfig-D6QndOCz.js} +1 -1
- package/dist/{ws-XngU-GGe.js → ws-dgltfu3A.js} +3 -3
- package/dist/{ws-B830G79O.js → ws-sf8JPYPq.js} +3 -3
- package/lab/ui/index.html +2 -1
- package/lab/ui/lab/build.ts +5 -4
- package/lab/ui/lab/index.md +14 -14
- package/lab/ui/lab/posts/2026-06-15-e2ee-share-links.html +128 -132
- package/lab/ui/lab/posts/2026-07-22-ay-callback.html +214 -167
- package/lab/ui/lab/posts/2026-08-11-an-address-space-for-agents.md +9 -9
- package/lab/ui/lab/posts/2026-08-11-no-daemon-owns-your-agents.md +13 -13
- package/lab/ui/lab/posts/2026-08-11-slack-term-safe-writes.md +26 -26
- package/lab/ui/lab/posts/2026-08-11-the-terminal-is-the-api.md +10 -10
- package/lab/ui/landing.html +2 -1
- package/package.json +4 -1
- package/ts/buildRustArgs.spec.ts +3 -0
- package/ts/cmdDsh.spec.ts +90 -0
- package/ts/cmdDsh.ts +62 -0
- package/ts/histStore.spec.ts +8 -4
- package/ts/histStore.ts +2 -7
- package/ts/identity.spec.ts +4 -1
- package/ts/initMsg.spec.ts +12 -2
- package/ts/invokedCli.spec.ts +1 -0
- package/ts/postbuild.ts +11 -8
- package/ts/subcommands.ts +6 -0
- package/ts/systemPathLink.spec.ts +12 -2
- package/ts/systemPathLink.ts +5 -1
- package/dist/trayApp-CUuEodHT.js +0 -5
package/lab/ui/lab/index.md
CHANGED
|
@@ -19,7 +19,7 @@ That sounds like tooling. It is closer to a change of category.
|
|
|
19
19
|
|
|
20
20
|
## Why that is not a small difference
|
|
21
21
|
|
|
22
|
-
Every agent CLI ships with the same assumption baked in:
|
|
22
|
+
Every agent CLI ships with the same assumption baked in: _a human is sitting right here._ It asks
|
|
23
23
|
before it edits. It draws a full-screen TUI for eyes that are watching. It dies when the terminal
|
|
24
24
|
closes. Those are reasonable defaults, and they are also a ceiling — they cap an agent at exactly
|
|
25
25
|
one person's attention, and attention is the scarcest thing in the building.
|
|
@@ -27,7 +27,7 @@ one person's attention, and attention is the scarcest thing in the building.
|
|
|
27
27
|
agent-yes removes the assumption in the least invasive way available: it does not fork the CLIs, it
|
|
28
28
|
does not need their APIs, and it does not wait for a vendor to ship a daemon mode. It runs the real
|
|
29
29
|
binary in a pseudo-terminal and answers the questions a human would have answered. The terminal
|
|
30
|
-
|
|
30
|
+
_is_ the integration surface.
|
|
31
31
|
|
|
32
32
|
What follows is the interesting part. Once one agent no longer needs a human in the loop, each
|
|
33
33
|
capability forces the next:
|
|
@@ -48,16 +48,16 @@ because paying those bills honestly is more interesting than the original trick.
|
|
|
48
48
|
The system reads cleanly top-down, and the
|
|
49
49
|
[interactive architecture map](https://agent-yes.com/architecture.html) draws the same boxes as a graph:
|
|
50
50
|
|
|
51
|
-
| Plane
|
|
52
|
-
|
|
|
53
|
-
| **① Agent core**
|
|
54
|
-
| **② Local fabric**
|
|
55
|
-
| **③ Exposure**
|
|
56
|
-
| **④ Trust & discovery** | Signaling that is a rendezvous only, an HKDF key split so the relay never holds a key, and signed capability tokens that carry their own scope.
|
|
57
|
-
| **⑤ Reach surfaces**
|
|
51
|
+
| Plane | What lives there |
|
|
52
|
+
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
53
|
+
| **① Agent core** | The PTY wrapper. Spawns any agent CLI, matches ready / enter / fatal patterns, restarts on crash, resumes sessions, propagates terminal size. Rust is the primary binary; TypeScript is the reference implementation. |
|
|
54
|
+
| **② Local fabric** | `pids.jsonl` (every agent, both runtimes), a FIFO per pid for stdin, a raw PTY log per agent for stdout. Files, not a daemon. |
|
|
55
|
+
| **③ Exposure** | One transport-agnostic handler, `apiFetch(req) → Response`. HTTP and the WebRTC bridge are both just callers; port tunnels and libp2p peers fold in next. |
|
|
56
|
+
| **④ Trust & discovery** | Signaling that is a rendezvous only, an HKDF key split so the relay never holds a key, and signed capability tokens that carry their own scope. |
|
|
57
|
+
| **⑤ Reach surfaces** | The browser console, the CLI on another box, embeddable widgets, a system tray, peers. |
|
|
58
58
|
|
|
59
59
|
The seam that holds it together is plane ③. Because `Bun.serve` and the WebRTC bridge already call
|
|
60
|
-
the
|
|
60
|
+
the _same in-process_ `apiFetch`, a new transport is a new caller and never a new control plane.
|
|
61
61
|
That is what turns "three stacks" back into one system.
|
|
62
62
|
|
|
63
63
|
## What is actually true today
|
|
@@ -81,12 +81,12 @@ about the four places the current system is still cluttered.
|
|
|
81
81
|
|
|
82
82
|
## The ecosystem
|
|
83
83
|
|
|
84
|
-
agent-yes is the fleet. Around it sit tools that give the fleet somewhere to
|
|
84
|
+
agent-yes is the fleet. Around it sit tools that give the fleet somewhere to _reach_ — each a
|
|
85
85
|
standalone CLI in its own right, each usable without the others:
|
|
86
86
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
| **[agent-yes](https://github.com/snomiao/agent-yes)**
|
|
87
|
+
| | |
|
|
88
|
+
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
89
|
+
| **[agent-yes](https://github.com/snomiao/agent-yes)** | run, watch, message and delegate to a fleet of AI coding agents |
|
|
90
90
|
| **[slack-term](https://github.com/snomiao/slack-term)** | the human-comms surface — read and write Slack from a terminal, with a confirm gate built for operators who are sometimes machines |
|
|
91
91
|
|
|
92
92
|
The connective tissue is a shared stance rather than a shared library: file-based state over
|
|
@@ -3,148 +3,144 @@ title: End-to-end encryption for agent-yes share links
|
|
|
3
3
|
tag: Security
|
|
4
4
|
summary: How share links stay private even if the signaling relay is compromised: an HKDF key split, AES-256-GCM with per-connection keys, and a fail-closed handshake.
|
|
5
5
|
---
|
|
6
|
+
|
|
6
7
|
<p class="lede">
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
agent-yes share links now run an end-to-end-encrypted protocol. The signaling server that
|
|
9
|
+
introduces your browser to your machine never sees a key — so even if it is fully compromised, it
|
|
10
|
+
cannot read your terminal, type into your agents, or spawn new ones.
|
|
11
|
+
</p>
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<pre><code>https://agent-yes.com/#<room>:e1.<secret></code></pre>
|
|
13
|
+
<h2>What a share link does</h2>
|
|
14
|
+
<p>
|
|
15
|
+
When you run <code>ay serve --webrtc</code>, your machine connects to a tiny Cloudflare signaling
|
|
16
|
+
server and waits for a browser to open the printed link on <code>agent-yes.com</code>. The
|
|
17
|
+
signaling server is only a <em>matchmaker</em>: it relays the WebRTC handshake (SDP + ICE) so the
|
|
18
|
+
two sides can find each other, then your terminal traffic flows <strong>peer-to-peer</strong> over
|
|
19
|
+
an encrypted WebRTC DataChannel — it never passes through the server. The room link looks like:
|
|
20
|
+
</p>
|
|
21
|
+
<pre><code>https://agent-yes.com/#<room>:e1.<secret></code></pre>
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
<h2>The threat: "what if the matchmaker is hacked?"</h2>
|
|
24
|
+
<p>
|
|
25
|
+
Previously, the link's token did double duty — it was both the value the server used to match
|
|
26
|
+
peers <em>and</em> the only shared secret. That meant the server <em>saw the secret</em>. A
|
|
27
|
+
compromised signaling server could therefore impersonate a browser or man-in-the-middle the
|
|
28
|
+
connection, and a breach would expose <em>every</em> user's token at once. The bar we want
|
|
29
|
+
instead:
|
|
30
|
+
</p>
|
|
31
|
+
<div class="note">
|
|
32
|
+
A fully compromised signaling server (or an active network attacker who can rewrite the relayed
|
|
33
|
+
handshake) may slow you down or learn metadata — but must
|
|
34
|
+
<strong>never</strong> read terminal I/O, inject input, spawn agents, or recover any key.
|
|
35
|
+
</div>
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
<h2>One secret, two derived keys (the HKDF split)</h2>
|
|
38
|
+
<p>
|
|
39
|
+
The link still carries one 32-byte secret <code>S</code>. We run it through HKDF-SHA256 to derive
|
|
40
|
+
two unrelated values:
|
|
41
|
+
</p>
|
|
42
|
+
<pre><code>authToken = HKDF(S, "ay/ay-e2e-1/auth") → the ONLY value the server sees
|
|
43
43
|
e2e keys = HKDF(S, "ay/ay-e2e-1/key/…") → never leave your machine or browser</code></pre>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
<p>
|
|
45
|
+
HKDF is one-way, so from <code>authToken</code> the server cannot recover <code>S</code> or the
|
|
46
|
+
encryption keys. The actual terminal frames are sealed with AES-256-GCM under keys the server has
|
|
47
|
+
never seen. Because the link is pasted directly between the two endpoints, both sides already
|
|
48
|
+
share <code>S</code> without the server ever being told it.
|
|
49
|
+
</p>
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
</p>
|
|
51
|
+
<h2>The subtle part nobody gets right: nonce reuse</h2>
|
|
52
|
+
<p>
|
|
53
|
+
AES-GCM is catastrophic if a <code>(key, nonce)</code> pair ever repeats — it leaks plaintext
|
|
54
|
+
<em>and</em> lets an attacker forge messages. The naive design (one key per room plus a
|
|
55
|
+
per-connection counter) reuses nonces across reconnects, host restarts, and multiple browsers in
|
|
56
|
+
the same room. Our fix: derive the encryption keys <strong>per connection</strong> by folding the
|
|
57
|
+
live DTLS handshake fingerprint into HKDF as the salt. Every session and every peer therefore gets
|
|
58
|
+
fresh keys, so a counter that restarts at 0 is always paired with a never-before-used key. We also
|
|
59
|
+
use
|
|
60
|
+
<strong>directional keys</strong>
|
|
61
|
+
(host→client and client→host are different keys), so the two senders never share a nonce space.
|
|
62
|
+
</p>
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
</p>
|
|
64
|
+
<h2>Binding to the real connection (anti-MITM), done right</h2>
|
|
65
|
+
<p>
|
|
66
|
+
We hash both peers' DTLS fingerprints — plus the handshake role and ICE ufrag — from the
|
|
67
|
+
negotiated session, and use that hash as <strong>both</strong> the HKDF salt for the keys
|
|
68
|
+
<strong>and</strong> the authenticated data on every frame. On top of that, the channel runs a
|
|
69
|
+
mandatory, bidirectional <strong>key-confirmation handshake</strong> (each side sends a fresh
|
|
70
|
+
challenge nonce and must see it echoed back) that has to complete in <em>both</em> directions
|
|
71
|
+
before a single byte of terminal I/O is processed. If a relay sat in the middle, the fingerprints
|
|
72
|
+
differ, the keys differ, confirmation fails, and the connection <span class="no">closes</span> —
|
|
73
|
+
there is no "connected but unverified" window and no silent fallback.
|
|
74
|
+
</p>
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
76
|
+
<h2>Replay, reordering, and forged aborts</h2>
|
|
77
|
+
<p>
|
|
78
|
+
The DataChannel is reliable and ordered, and we add a mandatory monotonic frame counter, 128-bit
|
|
79
|
+
random request ids, and per-stream sequence numbers. So a captured frame can't be replayed to
|
|
80
|
+
re-run a command, a stale "abort" can't cancel a fresh request, and a truncated stream can't
|
|
81
|
+
masquerade as a complete response.
|
|
82
|
+
</p>
|
|
85
83
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
<h2>Fail closed, always</h2>
|
|
85
|
+
<p>
|
|
86
|
+
Encrypted links carry a version marker (<code>#room:e1.<secret></code>). The grammar is
|
|
87
|
+
strict: anything that looks like a marker but isn't exactly <code>e1.</code> + 64 hex is rejected,
|
|
88
|
+
never quietly treated as a legacy link. Legacy plaintext is hard-disabled in the client, the host
|
|
89
|
+
refuses to bridge a plaintext channel for an encrypted room, and on any version skew, missing
|
|
90
|
+
marker, bad tag, fingerprint mismatch, or confirmation timeout we
|
|
91
|
+
<span class="no">refuse</span> rather than downgrade.
|
|
92
|
+
</p>
|
|
95
93
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
94
|
+
<h2>One implementation, no dependencies</h2>
|
|
95
|
+
<p>
|
|
96
|
+
The crypto is a single WebCrypto module shared by both ends — the browser imports it, and the host
|
|
97
|
+
(running on Bun, which ships WebCrypto) bundles the exact same file. One implementation means the
|
|
98
|
+
two sides can't drift apart, and there are no new dependencies. A test suite pins the key
|
|
99
|
+
derivation, the frame format, and every fail-closed path.
|
|
100
|
+
</p>
|
|
103
101
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
102
|
+
<h2>What a hacked server gets — and doesn't</h2>
|
|
103
|
+
<table>
|
|
104
|
+
<tr>
|
|
105
|
+
<th></th>
|
|
106
|
+
<th>Before</th>
|
|
107
|
+
<th>Now</th>
|
|
108
|
+
</tr>
|
|
109
|
+
<tr>
|
|
110
|
+
<td>Read your terminal I/O</td>
|
|
111
|
+
<td class="no">possible (sees the secret)</td>
|
|
112
|
+
<td class="ok">no — keys never reach the server</td>
|
|
113
|
+
</tr>
|
|
114
|
+
<tr>
|
|
115
|
+
<td>Inject input / spawn agents</td>
|
|
116
|
+
<td class="no">possible</td>
|
|
117
|
+
<td class="ok">no — frames it forges fail decryption</td>
|
|
118
|
+
</tr>
|
|
119
|
+
<tr>
|
|
120
|
+
<td>Harvest every user's secret in a breach</td>
|
|
121
|
+
<td class="no">yes (stores raw tokens)</td>
|
|
122
|
+
<td class="ok">no — only one-way authTokens</td>
|
|
123
|
+
</tr>
|
|
124
|
+
<tr>
|
|
125
|
+
<td>DoS / see metadata (rooms, timing, IPs)</td>
|
|
126
|
+
<td class="no">yes</td>
|
|
127
|
+
<td class="no">still yes</td>
|
|
128
|
+
</tr>
|
|
129
|
+
</table>
|
|
132
130
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
</p>
|
|
131
|
+
<h2>What changed for you</h2>
|
|
132
|
+
<p>
|
|
133
|
+
Old share links rotate to a fresh encrypted room automatically on upgrade — re-open the new
|
|
134
|
+
printed link (or <code>rm ~/.agent-yes/.share-room</code> to force a rotation). The signaling
|
|
135
|
+
server got one small additive change (it pins a protocol version) but still never sees a key.
|
|
136
|
+
</p>
|
|
140
137
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
</p>
|
|
138
|
+
<h2>Honest limitations</h2>
|
|
139
|
+
<p>
|
|
140
|
+
End-to-end encryption protects the <em>channel</em>, not the <em>capability</em>: anyone you hand
|
|
141
|
+
the full link to gets control, so treat it like an SSH key. The server can still deny service and
|
|
142
|
+
observe metadata (which rooms exist, connection timing, IPs via ICE). The channel binding uses
|
|
143
|
+
DTLS fingerprints from the SDP rather than a true RFC 5705 keying exporter (a limitation of the
|
|
144
|
+
host's WebRTC stack today), and the secret still sits in your browser's local storage until it
|
|
145
|
+
ages out. Forward secrecy / rekeying, and the separate codehost transport, are future work.
|
|
146
|
+
</p>
|