@sloev/trystero-corvax 0.0.0-stage → 0.1.17
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 +26 -2
- package/index.js +96 -0
- package/package.json +32 -4
package/README.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sloev/trystero-corvax
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[Trystero](https://github.com/dmotz/trystero) signalling over [CORVAX](https://github.com/sloev/CORVAX): no relay server, nothing to configure.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm i @trystero-p2p/core @sloev/trystero-corvax
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { joinRoom } from '@sloev/trystero-corvax'
|
|
11
|
+
|
|
12
|
+
const room = joinRoom({ appId: 'my-app' }, 'lobby')
|
|
13
|
+
room.onPeerJoin(({ peerId }) => console.log('joined', peerId))
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Browsers without a bundler: `import { joinRoom } from 'https://cdn.jsdelivr.net/npm/@sloev/trystero-corvax/+esm'`.
|
|
17
|
+
|
|
18
|
+
**How it signals** (peer to peer):
|
|
19
|
+
|
|
20
|
+
- **Members:** CORVAX topic presence on the room's topic: signed leases kept by many storage peers, not one server.
|
|
21
|
+
- **Announce:** flooded over the room's CORVAX mesh, and sealed straight to each member through that member's own inbox anchors.
|
|
22
|
+
- **Offers, answers, candidates:** over a CORVAX mesh link to that peer when there is one (no anchor at all), otherwise to its inboxes. Senders are verified by signature.
|
|
23
|
+
- **Once a room's mesh exists, it no longer needs any anchor:** reconnects re-signal over the mesh.
|
|
24
|
+
|
|
25
|
+
Trystero encrypts its signals with the room key, and CORVAX seals them again for the one recipient. Joining took 0.3 s between two Chromium tabs in our tests.
|
|
26
|
+
|
|
27
|
+
Options: `joinRoom({ appId, corvax: { peer, ...Peer.create options } }, roomId)`. Pass `corvax.peer` to share an existing CORVAX peer.
|
package/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @sloev/trystero-corvax: a Trystero signalling transport on CORVAX, peer to peer (SPEC §10).
|
|
2
|
+
// room members → CORVAX topic presence on the room's root topic (signed leases on storage peers, any peer)
|
|
3
|
+
// announce → a topic broadcast over the room's CORVAX mesh, and a sealed message to each member's inboxes
|
|
4
|
+
// signal → over a CORVAX mesh link to that peer when there is one (no anchor), else to its inboxes
|
|
5
|
+
// Trystero encrypts its signals with the room key; CORVAX seals them again for the one recipient.
|
|
6
|
+
import { createTopicStrategy } from '@trystero-p2p/core'
|
|
7
|
+
import { Peer } from '@sloev/corvax'
|
|
8
|
+
|
|
9
|
+
export { clearUserIdentity, createUserIdentity, exportUserIdentity, importUserIdentity, roles, scale } from '@trystero-p2p/core'
|
|
10
|
+
|
|
11
|
+
const peers = new Map() // options key → Promise<Peer>: one CORVAX peer per tab or process
|
|
12
|
+
const TAG = 'tr1'
|
|
13
|
+
|
|
14
|
+
// The CORVAX peer behind this transport: one per tab (sessionStorage seed), or pass your own as corvax.peer.
|
|
15
|
+
export function corvaxPeer ({ peer, ...options } = {}) {
|
|
16
|
+
if (peer) return Promise.resolve(peer)
|
|
17
|
+
const key = JSON.stringify(options)
|
|
18
|
+
if (!peers.has(key)) peers.set(key, (async () => {
|
|
19
|
+
let seed = null
|
|
20
|
+
try { seed = sessionStorage.getItem('corvax.trystero') } catch {}
|
|
21
|
+
const p = await Peer.create({ seed, ...options })
|
|
22
|
+
try { sessionStorage.setItem('corvax.trystero', p.seed) } catch {}
|
|
23
|
+
wire(p)
|
|
24
|
+
return p
|
|
25
|
+
})())
|
|
26
|
+
return peers.get(key)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Per CORVAX peer: Trystero topic → handlers; peer topic → CORVAX address (learned from signed messages).
|
|
30
|
+
function wire (p) {
|
|
31
|
+
if (p.trystero) return p.trystero
|
|
32
|
+
const s = p.trystero = { handlers: new Map(), routes: new Map(), rooms: new Map(), early: [] }
|
|
33
|
+
const deliver = (from, body) => {
|
|
34
|
+
if (body?.[TAG] !== 1 || typeof body.t !== 'string' || typeof body.st !== 'string') return
|
|
35
|
+
s.routes.set(body.st, from) // reply path: their topic → their verified address
|
|
36
|
+
const fns = s.handlers.get(body.t)
|
|
37
|
+
if (fns?.size) for (const fn of fns) fn(body.t, body.m)
|
|
38
|
+
else s.early = [...s.early.filter(e => Date.now() - e.at < 10e3), { at: Date.now(), body }].slice(-64) // before we subscribed
|
|
39
|
+
}
|
|
40
|
+
p.on(({ from, body }) => deliver(from, body))
|
|
41
|
+
s.deliver = deliver
|
|
42
|
+
return s
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// A CORVAX topic per Trystero root topic: members (presence), a mesh (links), and the last announce to repeat.
|
|
46
|
+
async function room (p, s, root) {
|
|
47
|
+
if (s.rooms.has(root)) return s.rooms.get(root)
|
|
48
|
+
const r = { members: new Set(), announce: null, topic: await p.topic('trystero:' + root) }
|
|
49
|
+
s.rooms.set(root, r)
|
|
50
|
+
r.off = r.topic.subscribe({
|
|
51
|
+
presence: e => {
|
|
52
|
+
if (e.presence !== 'recent') return r.members.delete(e.address)
|
|
53
|
+
if (r.members.has(e.address)) return
|
|
54
|
+
r.members.add(e.address)
|
|
55
|
+
if (r.announce) send(p, e.address, r.announce) // a newcomer hears from us at once, not in 60 s
|
|
56
|
+
},
|
|
57
|
+
message: m => s.deliver(m.from, m.body) // announces flooded over the room's mesh
|
|
58
|
+
})
|
|
59
|
+
await r.topic.publish()
|
|
60
|
+
return r
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const send = (p, address, body) => p.topics.sendVia(address, body) || p.send(address, body).catch(() => {})
|
|
64
|
+
|
|
65
|
+
export const joinRoom = createTopicStrategy({
|
|
66
|
+
init: config => corvaxPeer(config.corvax),
|
|
67
|
+
|
|
68
|
+
subscribeTopic: async (p, topic, onMessage, { kind, rootTopic }) => {
|
|
69
|
+
const s = wire(p)
|
|
70
|
+
if (!s.handlers.has(topic)) s.handlers.set(topic, new Set())
|
|
71
|
+
s.handlers.get(topic).add(onMessage)
|
|
72
|
+
for (const e of s.early.filter(e => e.body.t === topic && Date.now() - e.at < 10e3)) onMessage(topic, e.body.m)
|
|
73
|
+
s.early = s.early.filter(e => e.body.t !== topic)
|
|
74
|
+
if (kind === 'root') await room(p, s, rootTopic)
|
|
75
|
+
return () => {
|
|
76
|
+
s.handlers.get(topic)?.delete(onMessage)
|
|
77
|
+
if (kind !== 'root' || s.handlers.get(topic)?.size) return
|
|
78
|
+
const r = s.rooms.get(rootTopic)
|
|
79
|
+
s.rooms.delete(rootTopic)
|
|
80
|
+
r?.off(); r?.topic.close().catch(() => {})
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
publishTopic: async (p, topic, msg, { kind, rootTopic, selfTopic }) => {
|
|
85
|
+
const s = wire(p), body = { [TAG]: 1, t: topic, st: selfTopic, m: msg }
|
|
86
|
+
if (kind === 'announce') {
|
|
87
|
+
const r = await room(p, s, rootTopic)
|
|
88
|
+
r.announce = body
|
|
89
|
+
r.topic.broadcast(body).catch(() => {}) // over the mesh, if we have links
|
|
90
|
+
for (const address of r.members) send(p, address, body) // and straight to each member
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
const address = s.routes.get(topic) // signals answer someone we heard from
|
|
94
|
+
if (address) send(p, address, body)
|
|
95
|
+
}
|
|
96
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sloev/trystero-corvax",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.17",
|
|
4
|
+
"description": "Trystero signalling over CORVAX: peer to peer, no relay server; rooms keep re-signalling over their own mesh.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": "./index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@sloev/corvax": "^0.1.17"
|
|
13
|
+
},
|
|
14
|
+
"peerDependencies": {
|
|
15
|
+
"@trystero-p2p/core": ">=0.27.0 <0.28.0"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/sloev/CORVAX.git",
|
|
20
|
+
"directory": "trystero"
|
|
21
|
+
},
|
|
22
|
+
"homepage": "https://corvax.sloev.workers.dev/",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"trystero",
|
|
25
|
+
"webrtc",
|
|
26
|
+
"p2p",
|
|
27
|
+
"signalling",
|
|
28
|
+
"corvax"
|
|
29
|
+
],
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
}
|
|
34
|
+
}
|