1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
|
# keks-meet
a simple secure web conferencing application
## Features
- Ability to share different kinds of media:
- Camera
- Microphone
- Screen capture
- Files
- End-to-end-encryption (including p2p negotiation, chat and p2p traffic)
- Peer-to-peer data transmission
- Multiple streams of any type
- Noise suppression (using RNNoise)
- Rooms (created on demand, do not persist)
- Chat (supports text and images)
- Minimal user-interface
- Should work with screen readers
## Licence
Licensed under the terms of the GNU Affero General Public License version 3 only
with exception of the icons found withing `client-web/assets/icons`, which are
Apache-2.0 licensed. See [COPYING](./COPYING).
## Usage
For trying it out, a hosted version is available on
[meet.metamuffin.org](https://meet.metamuffin.org/). For self-hosting, this
should help:
```sh
pacman -S --needed esbuild rustup make coreutils curl tar; rustup install nightly
git clone https://codeberg.org/metamuffin/keks-meet.git
cd keks-meet
make install-server # binaries will be installed to ~/.cargo/bin
keks-meet-server config/default.toml
```
When changing code, use `make watch` to re-build things automatically as needed.
(requires `cargo install systemfd cargo-watch`)
The server takes a path to the configuration file as its first argument unless
the `embed_config` feature is used. In that case, the configuration is read from
`config/config.toml` and embedded into the server binary.
The server's bind address can be controlled using the `BIND` environment
variable. When compilin without debug assertions (release) all assets are
embedded into the binary; This is a speedup and allows the server to run from
just the binary.
If you use this project or have any suggestions, please
[contact me](https://metamuffin.org/contact)
## _Rift_
_Rift_ is similar to the
[magic wormhole](https://github.com/magic-wormhole/magic-wormhole), except that
it's peer-to-peer. It reuses the keks-meet signaling protocol to establish a
WebRTC data channel.
```sh
pacman -S --needed rustup; rustup install nightly
cargo +nightly install --path client-native-rift
rift --help
```
```sh
rift --secret hunter2 send /path/to/file &
rift --secret hunter2 receive /path/to/output
```
## Security
keks-meet _tries_ to be secure. However I am not a security expert. The current
system works as follows:
- The room name is set in the section of the URL which is not sent to the
server.
- The server receives a salted SHA-256 hash of the room name to group clients of
a room.
- The client uses PBKDF2 (constant salt; 250000 iterations) to derive a 256-bit
AES-GCM key from the room name.
- All relayed message contents are encrypted with this key.
- Message recipient is visible to the server
- The server assigns user ids
## Keybinds
| Keybind | Action |
| ------- | ------------------------------------------------------- |
| `C-RET` | Toggle chat |
| `M` | Add microphone track |
| `R` | Add microphone track (but with your left hand) |
| `C` | Add camera track |
| `S` | Add screencast track |
| `C-c` | End all tracks |
| `C-v`\* | Paste image in chat (does not require chat to be shown) |
## Debugging
- If a connection cant be established, look at it with `webrtc_debug` enabled.
- In case downloading files doesn't work, check if the service worker was
installed correctly by visiting `/swtest`
- If it still doesn't work, file a bug report.
## Parameters
Some configuration parameters can be added like query params but **after** the
section. (e.g `/room#mymeeting?username=alice`) The page will not automatically
reload if the section changes. Booleans can be either `1`, `true`, `yes` or
their opposites. A convenience function for changing params is also exported:
`window.change_pref(key, value)`
| Option name | Type | Default | Description |
| ------------------------------- | ------- | ----------- | ------------------------------------------------------------------------------ |
| `username` | string | `"guest-…"` | Username |
| `warn_redirect` | boolean | `false` | Internal option that is set by a server redirect. |
| `image_view_popup` | boolean | `true` | Open image in popup instead of new tab |
| `webrtc_debug` | boolean | `false` | Show additional information for WebRTC related stuff |
| `microphone_enabled` | boolean | `false` | Add one microphone track on startup |
| `screencast_enabled` | boolean | `false` | Add one screencast track on startup |
| `camera_enabled` | boolean | `false` | Add one camera track on startup |
| `rnnoise` | boolean | `true` | Use RNNoise for noise suppression |
| `native_noise_suppression` | boolean | `false` | Suggest the browser to do noise suppression |
| `microphone_gain` | number | `1` | Amplify microphone volume |
| `video_fps` | number | - | Preferred framerate (in 1/s) for screencast and camera |
| `video_resolution` | number | - | Preferred width for screencast and camera |
| `camera_facing_mode` | string | - | Prefer user-facing or env-facing camera (`"environment"` / `"user"`) |
| `auto_gain_control` | boolean | - | Automatically adjust mic gain |
| `echo_cancellation` | boolean | - | Cancel echo |
| `audio_activity_threshold` | number | `0.003` | Audio activity threshold |
| `optional_audio_default_enable` | boolean | `true` | Enable audio tracks by default |
| `optional_video_default_enable` | boolean | `false` | Enable video tracks by default |
| `notify_chat` | boolean | `true` | Send notifications for incoming chat messages |
| `notify_join` | boolean | `true` | Send notifications when users join |
| `notify_leave` | boolean | `true` | Send notifications when users leave |
| `enable_onbeforeunload` | boolean | `true` | Prompt for confirmation when leaving the site while local resources are active |
| `room_watches` | string | `"public"` | Known rooms (as semicolon seperated list of name=secret pairs) |
## Protocol
The protocol packets are defined in [packets.d.ts](./common/packets.d.ts). Here
are some simplified examples of how the protocol is used.
```
S->C { init: { your_id: 5, version: "..." } }
---- # Your join packet will be the first one.
S->C { client_join: { id: 5 } }
S->C { client_join: { id: 3 } }
---- # The server doesnt know people's names so they identify themselves.
S->C { message: { sender: 3, message: <Encrypted { identify: { username: "Alice" } }> } }
---- # You should do that too.
C->S { relay: { message: <Encrypted { identify: { username: "Bob" } }> } }
---- # Publish your ICE candidates.
C->S { relay: { message: <Encrypted { ice_candiate: <RTCIceCandidateInit> }> } }
---- # If you create a resource, tell others about it:
C->S { relay: { message: <Encrypted { provide: { id: "asd123", label: "Camera", kind: "track", track_kind: "video" } }> } }
---- # If somebody is interested in this resource, they will request you to transmit.
S->C { message: { sender: 3, message: <Encrypted { request: { id: "asd123" } }> } }
---- # Whenever you change your tracks/data channels:
---- # Send an offer to everybody
C->S { relay: <Encrypted { recipient: 3, offer: <RTCSessionDescriptionInit> }> }
---- # Await answer:
S->C { message: { sender: 3, message: <Encrypted { offer: <RTCSessionDescriptionInit> }> } }
---- # In case the server uses a reverse-proxy that disconnects inactive connections: Ping every 30s
C->S { ping: null }
```
If you decide to implement this protocol, please make sure it is compatible,
especially ensure that channels/tracks are only added on request and to not
reuse existing identifiers for new protocol packets.
|