@weaveprotocol/core 0.1.0 → 0.1.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.
Files changed (50) hide show
  1. package/README.md +127 -4
  2. package/dist/calls/calls.d.ts +141 -0
  3. package/dist/calls/calls.d.ts.map +1 -0
  4. package/dist/calls/calls.js +763 -0
  5. package/dist/calls/calls.js.map +1 -0
  6. package/dist/calls/index.d.ts +7 -0
  7. package/dist/calls/index.d.ts.map +1 -0
  8. package/dist/calls/index.js +6 -0
  9. package/dist/calls/index.js.map +1 -0
  10. package/dist/network/mesh.d.ts +6 -0
  11. package/dist/network/mesh.d.ts.map +1 -1
  12. package/dist/network/mesh.js +33 -2
  13. package/dist/network/mesh.js.map +1 -1
  14. package/dist/network/multi-signaling.d.ts.map +1 -1
  15. package/dist/network/multi-signaling.js +13 -0
  16. package/dist/network/multi-signaling.js.map +1 -1
  17. package/dist/network/rtc-transport.d.ts +3 -1
  18. package/dist/network/rtc-transport.d.ts.map +1 -1
  19. package/dist/network/rtc-transport.js +4 -3
  20. package/dist/network/rtc-transport.js.map +1 -1
  21. package/dist/network/signaling.d.ts +8 -1
  22. package/dist/network/signaling.d.ts.map +1 -1
  23. package/dist/network/signaling.js +20 -0
  24. package/dist/network/signaling.js.map +1 -1
  25. package/dist/node/node.d.ts.map +1 -1
  26. package/dist/node/node.js +38 -4
  27. package/dist/node/node.js.map +1 -1
  28. package/dist/node/space-runtime.d.ts +2 -0
  29. package/dist/node/space-runtime.d.ts.map +1 -1
  30. package/dist/node/space-runtime.js +83 -2
  31. package/dist/node/space-runtime.js.map +1 -1
  32. package/dist/node/types.d.ts +49 -4
  33. package/dist/node/types.d.ts.map +1 -1
  34. package/dist/react/index.d.ts +2 -1
  35. package/dist/react/index.d.ts.map +1 -1
  36. package/dist/react/index.js +2 -1
  37. package/dist/react/index.js.map +1 -1
  38. package/dist/react/use-calls.d.ts +21 -0
  39. package/dist/react/use-calls.d.ts.map +1 -0
  40. package/dist/react/use-calls.js +38 -0
  41. package/dist/react/use-calls.js.map +1 -0
  42. package/dist/react/use-space.d.ts +2 -2
  43. package/dist/react/use-space.d.ts.map +1 -1
  44. package/dist/react/use-space.js +9 -4
  45. package/dist/react/use-space.js.map +1 -1
  46. package/dist/schemas/index.d.ts +52 -0
  47. package/dist/schemas/index.d.ts.map +1 -1
  48. package/dist/schemas/index.js +23 -1
  49. package/dist/schemas/index.js.map +1 -1
  50. package/package.json +11 -2
package/README.md CHANGED
@@ -142,6 +142,86 @@ Every operation is also described in `NODE_ACTIONS` — a name, a sentence and a
142
142
  JSON Schema for its input — which is what the CLI, MCP and WebMCP front ends are
143
143
  generated from. `runAction(node, 'records_put', { … })` runs one by name.
144
144
 
145
+ ### Live messages, and spaces kept open
146
+
147
+ Some things should reach whoever is connected right now and be kept nowhere:
148
+ typing, presence, setting up a call. `spaces.send` is for those:
149
+
150
+ ```typescript
151
+ await node.spaces.send(space.id, { type: 'typing' }); // everyone connected in the space
152
+ await node.spaces.send(space.id, { type: 'nudge' }, anna); // only Anna's devices (her account DID)
153
+ await node.spaces.send(space.id, { type: 'hi' }, annasLaptop); // one device (its session DID)
154
+
155
+ node.subscribe((event) => {
156
+ if (event.type === 'message') event.from; event.peer; event.message; // account, device, what was sent
157
+ });
158
+ ```
159
+
160
+ A live message is never signed as a record, never written and never synced;
161
+ a peer who isn't connected misses it. It travels over the space's own peer
162
+ connections, so in a private space only people holding its read key can
163
+ receive one. At most 64 KB each, and each peer gets a burst of 60 and 20 a
164
+ second after that.
165
+
166
+ `event.from` is the **account**, not just a key. When two peers connect in a
167
+ space, each sends the other its note — the delegation from its account to its
168
+ session key, the same one its records carry. The note names the key it was
169
+ made out to, and that has to be the key the connection proved, so a note
170
+ copied off someone's record is no use to anyone else. `spaces.status(id).accounts`
171
+ lists who is connected, by account. A peer that shows no note (a carrier, a
172
+ node serving sockets) is `from: null`.
173
+
174
+ To keep a space syncing, **hold** it, and let go when you're done:
175
+
176
+ ```typescript
177
+ const release = await node.spaces.hold(space.id);
178
+ await release();
179
+ ```
180
+
181
+ Anything that needs a space live holds it: a screen showing it, a call in it.
182
+ It stops syncing once nothing does, so a screen going away never cuts off a
183
+ call. Each hold lets go only of itself, and letting go twice does nothing.
184
+ Reading or writing needs no hold. In React, `useHoldSpace(space)` holds a
185
+ space while a view is on screen, and lets go a few seconds late so that
186
+ clicking away and straight back doesn't rebuild the space's sync.
187
+
188
+ ### Calls (`@weaveprotocol/core/calls`)
189
+
190
+ Voice and video between the members of a space, built only from live
191
+ messages and the browser's WebRTC:
192
+
193
+ ```typescript
194
+ import { createCalls } from '@weaveprotocol/core/calls';
195
+
196
+ const calls = createCalls(node);
197
+ await calls.start(space.id, { video: true }); // join the call going on in the space, or start one
198
+ await calls.ring(space.id, anna); // start one, and ring Anna's devices
199
+ calls.subscribe(() => draw(calls.getState())); // { current, ringing, around, rejoin }
200
+ await calls.answer(ringing.id); calls.setMuted(true); await calls.shareScreen(); await calls.leave();
201
+ ```
202
+
203
+ - **A call belongs to a space, not to a screen.** It keeps its space open for
204
+ as long as it runs, so people can move between spaces and keep talking. One
205
+ call at a time. In React, `<CallsProvider>` above whatever changes as people
206
+ move around, and `useCalls()` below it.
207
+ - **Only members take part.** Someone with no role in the space (a view-only
208
+ reader) is never rung by, counted in or connected to a call.
209
+ - **Group calls don't ring.** A call going on shows in `around` for everyone
210
+ with the space open; only `ring`, aimed at one person, rings. One account
211
+ can ring you three times a minute.
212
+ - **Everyone connects to everyone**, each pair on a WebRTC connection of its
213
+ own, apart from the one sync uses. Good up to about six people with video.
214
+ The setup goes over the space's peer connection, whose handshake proved who
215
+ is at the other end, so a relay can't put itself in the middle of a call.
216
+ - **History, if the space wants it.** Where the space defines `std.call`, a
217
+ ring nobody answered leaves a `missed` record, and the last one out of a
218
+ call writes who was in it. Nothing else about a call is stored.
219
+ - **TURN.** `node.iceServers()` gives the configured ICE servers plus TURN
220
+ servers a relay hands out (see the relay, below), with passwords fresh for
221
+ a while. A call asks for them when it starts.
222
+
223
+ The messages, and the reasoning, are at the top of `src/calls/calls.ts`.
224
+
145
225
  ## Signing in — the element, and React
146
226
 
147
227
  Getting to a node takes a sign-in flow: where the data lives (a pod or this
@@ -208,7 +288,7 @@ Everything below the provider asks for what it needs:
208
288
  | `useRecord(space, key)` / `useLinked(space, key)` | One record; what points at it |
209
289
  | `useCollections(space)` / `useProfiles(space)` / `useSpaceStatus(space)` | What a space holds, who is in it, whether it is connected |
210
290
  | `useCan(space, action, target)` | Whether this account may create, edit or delete — for hiding a button |
211
- | `useOpenSpace(space)` | Keeps a space syncing while a view is on screen |
291
+ | `useHoldSpace(space)` | Keeps a space syncing while a view is on screen |
212
292
  | `useLive(space, load, deps)` | Anything else, reloaded as the space changes |
213
293
 
214
294
  An app connected to an account home passes its node instead:
@@ -659,8 +739,10 @@ Browser-to-browser communication via WebRTC.
659
739
 
660
740
  #### Signaling relay
661
741
 
662
- `server/signaling-server.mjs` is a dumb relay in a couple hundred lines of
663
- Node on the `ws` library: a peer holds one socket and joins a room on it for
742
+ `server/relay.mjs` is a dumb relay in a couple hundred lines of Node with no
743
+ dependencies of its own. It runs on its own as `server/signaling-server.mjs`
744
+ (an HTTP server and the `ws` library around it), and inside every always-on
745
+ node (`weave serve`), so the two are the same relay. A peer holds one socket and joins a room on it for
664
746
  each space, and the relay passes join notices and WebRTC offers, answers and
665
747
  candidates between peers that share a room. (A socket opened with `?room=` is
666
748
  the older one-room form, still served.) The room is a hash of
@@ -677,6 +759,21 @@ it forwards carries the sender's DID as it joined, whatever the message says.
677
759
  npm run signal # ws://localhost:8787; /health says {"ok":true}
678
760
  ```
679
761
 
762
+ It can also hand out **TURN** passwords, for calls between people on networks
763
+ that won't let two browsers connect directly. Run coturn with
764
+ `use-auth-secret`, and give the relay the same secret:
765
+
766
+ ```bash
767
+ TURN_SECRET=… TURN_URLS=turn:turn.example.com:3478,turns:turn.example.com:5349 npm run signal
768
+ ```
769
+
770
+ Each socket that joins a room gets servers and a password that stops working
771
+ after `TURN_TTL_SECONDS` (4 hours by default). The password is an HMAC of when
772
+ it expires (the "TURN REST API" scheme), so nothing is stored. The relay can't
773
+ tell a Weave peer from anyone else, so set coturn's own quotas (`user-quota`,
774
+ `total-quota`, `max-bps`). TURN only forwards encrypted packets; it can't
775
+ see or hear a call.
776
+
680
777
  Only peers already in a room hear about a newcomer, so exactly one side creates
681
778
  the offer and the two never collide.
682
779
 
@@ -1000,7 +1097,7 @@ the protocol straight from `src/`. It knows no kinds of data in advance: every
1000
1097
  screen is worked out from what a space says about itself (see *Derived UI* below):
1001
1098
 
1002
1099
  ```bash
1003
- npm install && (cd example && npm install) && (cd home && npm install) && (cd cli && npm install)
1100
+ npm install && (cd example && npm install) && (cd home && npm install) # the CLI is a workspace: the first one installs it
1004
1101
  npm run dev
1005
1102
  ```
1006
1103
 
@@ -1055,6 +1152,17 @@ tally. The helpers that work this out are pure functions
1055
1152
  empty space offers a small "define a collection" form; an agent can do the
1056
1153
  same over WebMCP.
1057
1154
 
1155
+ **Calls.** Every space you have a role in has "Start a call" at the top, or
1156
+ "Join call · 3" while one is going on, and People & roles has "Call" beside
1157
+ each member, which rings them. The call sits in a panel in the corner, over
1158
+ whatever page you're on: open other spaces, go back to the list, and you keep
1159
+ talking. The sidebar marks the space a call is in. The panel grows to fill the
1160
+ screen, or moves into a picture-in-picture window where the browser has one,
1161
+ so it stays in view when you switch tabs. Someone ringing you shows as a card
1162
+ on any page. The **Calls** app keeps a log of a space's calls (`std.call`).
1163
+ The calls themselves are `createCalls` (above), made once in `App.tsx`,
1164
+ above everything that changes as you move around.
1165
+
1058
1166
  **Agents in the browser (WebMCP).** When `/app` loads, it registers
1059
1167
  every node operation as a WebMCP tool on `document.modelContext`
1060
1168
  (`example/src/webmcp.ts`, with `@mcp-b/webmcp-polyfill`: Chrome's own WebMCP
@@ -1084,6 +1192,21 @@ with every tab closed. What it writes shows "via agent", and every peer
1084
1192
  ignores an agent changing collections, who may do what, or the account's own
1085
1193
  list of spaces. See BLOCK-20.
1086
1194
 
1195
+ ## Releasing
1196
+
1197
+ `@weaveprotocol/core` (this folder) and `@weaveprotocol/cli` (`cli/`, an npm
1198
+ workspace) are released together, always with the same version:
1199
+
1200
+ ```bash
1201
+ npm run release
1202
+ ```
1203
+
1204
+ It typechecks and runs the tests, then [bumpp](https://github.com/antfu-collective/bumpp)
1205
+ asks for the next version, writes it to both packages, and commits and tags
1206
+ it (`v0.1.2`). Both are published (each builds itself first: `dist/` for the
1207
+ core, one bundled file for the CLI), and only then is the commit pushed. The
1208
+ settings are in `bump.config.ts`.
1209
+
1087
1210
  ## Tests
1088
1211
 
1089
1212
  ```bash
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @module calls
3
+ * Voice and video calls between the people in a space.
4
+ *
5
+ * Nothing here is new to the protocol. A call is live messages
6
+ * (`node.spaces.send`) in the space it belongs to, and a WebRTC connection of
7
+ * its own between each pair of people in it — the shape Matrix gives group
8
+ * calls (MSC3401, Element Call's full mesh):
9
+ *
10
+ * ```
11
+ * call.here { call, since, camera, muted } to the space, every few seconds while in it
12
+ * call.ring { call } to one account: ring its devices
13
+ * call.answered { call } / call.declined to the caller, and to your own other devices
14
+ * call.cancel { call } the caller gave up ringing
15
+ * call.signal { call, description | candidate } to one device: setting up the connection
16
+ * call.leave { call } to the space: gone now, not in 15 seconds
17
+ * ```
18
+ *
19
+ * The setup travels over the space's own peer connection, whose handshake
20
+ * proved who is at the other end — so the offer, and the fingerprint of the
21
+ * key the browser will insist on, really come from that person. A relay only
22
+ * ever introduced the two devices; it never sees a call.
23
+ *
24
+ * Only members of the space (anyone holding a role) are rung by, heard in or
25
+ * connected to a call; a view-only reader of a public space is not.
26
+ *
27
+ * A call belongs to the space, not to a screen: it keeps the space open
28
+ * (`spaces.hold`) for as long as it runs, so moving between spaces
29
+ * never interrupts it. One call at a time.
30
+ *
31
+ * Each connection is made with an audio and a video transceiver from the
32
+ * start. Turning the camera on or off, or sharing the screen, swaps the track
33
+ * a sender carries and never renegotiates — so neither side can offer at the
34
+ * same moment as the other, and the side with the lower session DID always
35
+ * makes the offer.
36
+ */
37
+ import type { P2PNode } from '../node/types.js';
38
+ export interface CallsOptions {
39
+ /** How a WebRTC connection is made. `new RTCPeerConnection` by default. */
40
+ readonly createConnection?: (config: RTCConfiguration) => RTCPeerConnection;
41
+ /** Camera and microphone. `navigator.mediaDevices.getUserMedia` by default. */
42
+ readonly getUserMedia?: (constraints: MediaStreamConstraints) => Promise<MediaStream>;
43
+ /** The screen, for sharing it. `navigator.mediaDevices.getDisplayMedia` by default. */
44
+ readonly getDisplayMedia?: (constraints: DisplayMediaStreamOptions) => Promise<MediaStream>;
45
+ /** How a set of tracks becomes something a `<video>` can show. `new MediaStream` by default. */
46
+ readonly createStream?: (tracks: ReadonlyArray<MediaStreamTrack>) => MediaStream | null;
47
+ /** Where the call you're in is remembered, to offer rejoining after a reload. `sessionStorage` by default; null for nowhere. */
48
+ readonly storage?: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'> | null;
49
+ /** How often you say you're in a call. Default 5 s. */
50
+ readonly heartbeatMs?: number;
51
+ /** How long without hearing that someone is gone. Default 15 s. */
52
+ readonly goneMs?: number;
53
+ /** How long a ring rings before it's a missed call. Default 45 s. */
54
+ readonly ringMs?: number;
55
+ }
56
+ /** Someone else in the call you're in: one device of theirs */
57
+ export interface CallPeer {
58
+ /** Their device's session DID */
59
+ readonly peer: string;
60
+ /** Their account */
61
+ readonly account: string;
62
+ /** What they send: sound, and video when their camera is on. Null until it arrives. */
63
+ readonly stream: MediaStream | null;
64
+ readonly muted: boolean;
65
+ readonly camera: boolean;
66
+ readonly connection: 'connecting' | 'connected' | 'failed';
67
+ }
68
+ /** The call you're in */
69
+ export interface CurrentCall {
70
+ readonly id: string;
71
+ readonly space: string;
72
+ /** When you joined it, ms */
73
+ readonly joinedAt: number;
74
+ /** Your own camera and microphone, for showing yourself */
75
+ readonly local: MediaStream | null;
76
+ readonly muted: boolean;
77
+ readonly camera: boolean;
78
+ readonly sharing: boolean;
79
+ readonly people: ReadonlyArray<CallPeer>;
80
+ /** Who you're ringing, while it rings — and what came of it, for a moment after */
81
+ readonly outgoing: {
82
+ readonly to: string;
83
+ readonly since: number;
84
+ readonly state: 'ringing' | 'declined' | 'missed';
85
+ } | null;
86
+ /** Something went wrong that you should hear about: no microphone, say */
87
+ readonly problem: string | null;
88
+ }
89
+ /** Someone ringing you */
90
+ export interface IncomingCall {
91
+ readonly id: string;
92
+ readonly space: string;
93
+ /** The caller's account */
94
+ readonly from: string;
95
+ readonly since: number;
96
+ }
97
+ /** A call going on in a space you have open, that you're not in */
98
+ export interface CallAround {
99
+ readonly id: string;
100
+ readonly space: string;
101
+ /** Accounts in it, each once */
102
+ readonly people: ReadonlyArray<string>;
103
+ /** When the first of them joined, ms */
104
+ readonly since: number;
105
+ }
106
+ export interface CallsState {
107
+ readonly current: CurrentCall | null;
108
+ readonly ringing: ReadonlyArray<IncomingCall>;
109
+ readonly around: ReadonlyArray<CallAround>;
110
+ /** The call you were in before this page loaded, while it's still going on */
111
+ readonly rejoin: CallAround | null;
112
+ }
113
+ export interface CallOptions {
114
+ /** Start with the camera on. Default false: sound only. */
115
+ readonly video?: boolean;
116
+ }
117
+ export interface Calls {
118
+ /** What's going on now. The same object until something changes. */
119
+ getState(): CallsState;
120
+ subscribe(listener: () => void): () => void;
121
+ /** Joins the call going on in a space, or starts one. Leaves any other call first. */
122
+ start(space: string, options?: CallOptions & {
123
+ readonly call?: string;
124
+ }): Promise<void>;
125
+ /** Starts a call in a space, and rings one person there — all their devices, until one answers */
126
+ ring(space: string, account: string, options?: CallOptions): Promise<void>;
127
+ answer(id: string, options?: CallOptions): Promise<void>;
128
+ decline(id: string): Promise<void>;
129
+ leave(): Promise<void>;
130
+ setMuted(muted: boolean): void;
131
+ setCamera(on: boolean): Promise<void>;
132
+ /** Shows your screen in place of your camera, until you stop it (or the browser does) */
133
+ shareScreen(): Promise<void>;
134
+ stopSharing(): Promise<void>;
135
+ /** Stops offering `rejoin` */
136
+ forgetRejoin(): void;
137
+ /** Leaves any call and stops listening */
138
+ close(): Promise<void>;
139
+ }
140
+ export declare function createCalls(node: P2PNode, options?: CallsOptions): Calls;
141
+ //# sourceMappingURL=calls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calls.d.ts","sourceRoot":"","sources":["../../src/calls/calls.ts"],"names":[],"mappings":"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"}