ciphermesh 2.8.0 → 2.10.0
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/CHANGELOG.md +51 -0
- package/README.md +94 -91
- package/README.pt-BR.md +91 -90
- package/docs/PLUGINS.md +53 -14
- package/docs/commands.json +640 -0
- package/docs/demo.svg +2 -2
- package/package.json +3 -2
- package/src/client/ChatController.js +14 -10
- package/src/client/UI.js +4 -1
- package/src/client/index.js +6 -1
- package/src/p2p/P2PChatController.js +78 -11
- package/src/p2p/index.js +6 -1
- package/src/server/ConnectionGuard.js +158 -0
- package/src/server/WebSocketServer.js +35 -0
- package/src/server/config.js +15 -0
- package/src/server/index.js +18 -0
- package/src/server/preflight.js +96 -0
- package/src/shared/PluginManager.js +103 -30
- package/src/shared/config.js +12 -0
- package/src/shared/constants.js +15 -0
- package/src/shared/pluginCommand.js +106 -0
package/README.pt-BR.md
CHANGED
|
@@ -41,28 +41,28 @@ forwarding, imune a CGNAT).
|
|
|
41
41
|
|
|
42
42
|
## ✨ Destaques
|
|
43
43
|
|
|
44
|
-
| | Feature
|
|
45
|
-
|
|
46
|
-
| 🔐
|
|
47
|
-
| 🔄
|
|
48
|
-
| 🛡️
|
|
49
|
-
| 🕶️
|
|
50
|
-
| 🕵️
|
|
51
|
-
| 🌐
|
|
52
|
-
| 📨
|
|
53
|
-
| ✓✓
|
|
54
|
-
| 🗂️
|
|
55
|
-
| 🖼️
|
|
56
|
-
| 📎
|
|
57
|
-
| 💬
|
|
58
|
-
| 🎞️
|
|
59
|
-
| 👻
|
|
60
|
-
| 🔒
|
|
61
|
-
| 🗂️
|
|
62
|
-
| 🩺
|
|
63
|
-
| 🔐
|
|
64
|
-
| 🛰️
|
|
65
|
-
| 🧩
|
|
44
|
+
| | Feature | Resumo |
|
|
45
|
+
| --- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| 🔐 | **E2EE de verdade** | Curve25519 + XSalsa20-Poly1305 via libsodium, chaves em `sodium_malloc` — nunca tocam o disco |
|
|
47
|
+
| 🔄 | **Perfect Forward Secrecy** | Double Ratchet: uma chave por mensagem — comprometer hoje ≠ ler ontem |
|
|
48
|
+
| 🛡️ | **Pós-quântico híbrido** | X25519 **+ ML-KEM-768** misturado na raiz do ratchet — vence o "grava hoje, decifra depois" mantendo segurança ≥ à clássica ([detalhes](docs/ARCHITECTURE.md)) |
|
|
49
|
+
| 🕶️ | **Resistência a metadados** | **Sealed sender** — o relay nunca vê quem enviou a mensagem — + padding de comprimento em buckets fixos em todo ciphertext e cover traffic opcional (`/cover`) |
|
|
50
|
+
| 🕵️ | **TOFU + SAS** | Alarme de troca de chave (MITM), código de 6 dígitos verificável por voz e badges de confiança **✓/✗** inline ao lado dos nomes |
|
|
51
|
+
| 🌐 | **LAN e internet** | Detecta Tailscale sozinho e mostra o endereço alcançável no banner |
|
|
52
|
+
| 📨 | **Convites com QR** | `/invite` gera uma string `ciphermesh://` + QR — colou, caiu na sala certa |
|
|
53
|
+
| ✓✓ | **Read receipts cifrados** | O ✓✓ viaja como ciphertext comum — o servidor não distingue de mensagem |
|
|
54
|
+
| 🗂️ | **Histórico local cifrado** | Opt-in (só com passphrase), Argon2id + XSalsa20-Poly1305, `/search` e `/export` |
|
|
55
|
+
| 🖼️ | **Preview de imagens** | Fotos recebidas renderizam no chat em half-blocks coloridos |
|
|
56
|
+
| 📎 | **Transferências com resume** | Chunks perdidos são re-pedidos; reconexão retoma de onde parou |
|
|
57
|
+
| 💬 | **Cara de app moderno** | Suas mensagens à direita, avatar de emoji por usuário, reply com citação, `:fire:` → 🔥 |
|
|
58
|
+
| 🎞️ | **Interface animada** | Splash na abertura, spinner de reconexão, barra de transferência viva (shimmer + ETA), cadeado fechando no handshake e um selo pulsante "novas mensagens ↓" |
|
|
59
|
+
| 👻 | **Deniable e efêmeras** | Modo de negação plausível (crypto simétrica); mensagens efêmeras _queimam_ caractere a caractere ao expirar |
|
|
60
|
+
| 🔒 | **Salas privadas** | `/create <sala> <senha>` — zero-knowledge: a senha nunca sai da sua máquina (Argon2id → challenge-response Ed25519) e o conteúdo da sala ganha uma camada simétrica extra que nem um relay malicioso atravessa |
|
|
61
|
+
| 🗂️ | **Buffers multi-sala** | Fique em várias salas ao mesmo tempo — **Alt+1..9** alterna, com não-lidas por sala. A qual sala cada mensagem pertence viaja _dentro_ do payload cifrado: o relay nunca fica sabendo |
|
|
62
|
+
| 🩺 | **Ele se explica sozinho** | `/doctor` diagnostica uma conexão que falha camada por camada — endereço, DNS, TCP, TLS, protocolo — e diz o que fazer em cada falha |
|
|
63
|
+
| 🔐 | **Trava de tela** | `/lock` e `/autolock` põem a sessão atrás da sua passphrase quando você sai da frente; o `/panic` continua ali para o pior momento |
|
|
64
|
+
| 🛰️ | **Modo P2P sem servidor** | Descoberta de peers via mDNS na LAN — sem relay nenhum, e com quase o mesmo conjunto de comandos |
|
|
65
|
+
| 🧩 | **Plugins** | Solta um arquivo JS em `~/.ciphermesh/plugins` e ganha comandos novos — exemplos `/roll` e `/poll` inclusos ([API de plugins](docs/PLUGINS.md)) |
|
|
66
66
|
|
|
67
67
|
## 🚀 Começando
|
|
68
68
|
|
|
@@ -132,10 +132,10 @@ do
|
|
|
132
132
|
> nunca embutiram o addon nativo e só rodavam na máquina que os construiu, então
|
|
133
133
|
> aqueles anexos foram removidos.
|
|
134
134
|
|
|
135
|
-
| Binário
|
|
136
|
-
|
|
137
|
-
| `ciphermesh-<plataforma>`
|
|
138
|
-
| `ciphermesh-server-<plataforma>` | Só o relay, para quem hospeda e não quer mais nada na máquina.
|
|
135
|
+
| Binário | O que é |
|
|
136
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
137
|
+
| `ciphermesh-<plataforma>` | Tudo: cliente, relay e P2P. `ciphermesh server` e `ciphermesh p2p` funcionam igual ao npm. |
|
|
138
|
+
| `ciphermesh-server-<plataforma>` | Só o relay, para quem hospeda e não quer mais nada na máquina. |
|
|
139
139
|
|
|
140
140
|
**Todo mundo** (incluindo quem hospeda):
|
|
141
141
|
|
|
@@ -202,38 +202,39 @@ aquela para a qual este software foi feito.
|
|
|
202
202
|
<details>
|
|
203
203
|
<summary><b>Essenciais</b></summary>
|
|
204
204
|
|
|
205
|
-
| Comando
|
|
206
|
-
|
|
207
|
-
| `/help`
|
|
208
|
-
| `/tips`
|
|
209
|
-
| `/users`
|
|
210
|
-
| `/msg <nick> <texto>`
|
|
211
|
-
| `/reply <texto>`
|
|
212
|
-
| `/me <ação>`
|
|
205
|
+
| Comando | Descrição |
|
|
206
|
+
| ----------------------------- | ----------------------------------------------------------------------- |
|
|
207
|
+
| `/help` | Todos os comandos |
|
|
208
|
+
| `/tips` | Mostra uma dica rotativa de segurança/UX |
|
|
209
|
+
| `/users` | Quem está online (com away/status) |
|
|
210
|
+
| `/msg <nick> <texto>` | Mensagem privada (DM) |
|
|
211
|
+
| `/reply <texto>` | Responde citando a última mensagem recebida |
|
|
212
|
+
| `/me <ação>` | Ação em terceira pessoa — _«ana está compilando»_ |
|
|
213
213
|
| `/watch [add\|remove\|clear]` | Alerta quando uma palavra aparece em **qualquer** sala, como uma menção |
|
|
214
|
-
| `/invite [host:porta]`
|
|
215
|
-
| `/nick <novo>`
|
|
216
|
-
| `/quit`
|
|
214
|
+
| `/invite [host:porta]` | Gera convite `ciphermesh://` + QR code |
|
|
215
|
+
| `/nick <novo>` | Troca de apelido (antes de entrar — recupera de "apelido em uso") |
|
|
216
|
+
| `/quit` | Sair |
|
|
217
217
|
|
|
218
218
|
</details>
|
|
219
219
|
|
|
220
220
|
<details>
|
|
221
221
|
<summary><b>Salas</b></summary>
|
|
222
222
|
|
|
223
|
-
| Comando
|
|
224
|
-
|
|
225
|
-
| `/join <sala> [senha]`
|
|
226
|
-
| `/leave [sala]`
|
|
227
|
-
| `/create <sala> <senha>`
|
|
228
|
-
| `/rooms`
|
|
229
|
-
| `/room`
|
|
230
|
-
| `/topic [texto\|clear]`
|
|
231
|
-
| `/owner`
|
|
232
|
-
| `/kick` `/mute` `/ban`
|
|
223
|
+
| Comando | Descrição |
|
|
224
|
+
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
225
|
+
| `/join <sala> [senha]` | Abre a sala como um **novo buffer** — você continua nas outras (estilo IRC) |
|
|
226
|
+
| `/leave [sala]` | Sai de uma sala; o buffer fecha (a última sala é protegida) |
|
|
227
|
+
| `/create <sala> <senha>` | Cria uma **sala privada** 🔒 — veja abaixo |
|
|
228
|
+
| `/rooms` | Lista salas (🔒 marca as privadas) |
|
|
229
|
+
| `/room` | Sala atual + sua lista de buffers |
|
|
230
|
+
| `/topic [texto\|clear]` | Mostra ou define o assunto da sala — aparece na barra de status e é sincronizado para quem entra depois |
|
|
231
|
+
| `/owner` | Dono da sala |
|
|
232
|
+
| `/kick` `/mute` `/ban` | Moderação (dono da sala) — presa à chave pública, então trocar de apelido não desfaz um ban |
|
|
233
|
+
| `/block` `/unblock` `/blocklist` | Pare de ver alguém, **só para você**. Nada é enviado, o relay nunca fica sabendo e a pessoa não é avisada — por isso qualquer um pode usar, inclusive na `general`, que não tem dono. Funciona no P2P também, onde não há moderação nenhuma. |
|
|
233
234
|
|
|
234
235
|
**Buffers:** esteja em várias salas ao mesmo tempo — **Alt+1..9** alterna, e a
|
|
235
236
|
barra de status mostra `[1:general] [2:dev •3]` com não-lidas por sala. Como o
|
|
236
|
-
relay é cego (sealed sender), a qual sala cada mensagem pertence viaja
|
|
237
|
+
relay é cego (sealed sender), a qual sala cada mensagem pertence viaja _dentro_
|
|
237
238
|
do payload cifrado — o servidor nunca fica sabendo.
|
|
238
239
|
|
|
239
240
|
**Salas privadas** são zero-knowledge: a senha nunca sai da sua máquina. Ao
|
|
@@ -249,22 +250,22 @@ sem verificar não leria uma palavra. Combine a senha por outro canal.
|
|
|
249
250
|
<details>
|
|
250
251
|
<summary><b>Confiança & segurança</b></summary>
|
|
251
252
|
|
|
252
|
-
| Comando
|
|
253
|
-
|
|
254
|
-
| `/fingerprint [nick]`
|
|
255
|
-
| `/verify <nick>`
|
|
256
|
-
| `/verify-confirm <nick>`
|
|
257
|
-
| `/backup [caminho]`
|
|
258
|
-
| `/trust <nick>` / `/trustlist`
|
|
259
|
-
| `/contacts [add\|remove\|all]`
|
|
260
|
-
| `/deniable [on\|off]`
|
|
253
|
+
| Comando | Descrição |
|
|
254
|
+
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
+
| `/fingerprint [nick]` | Fingerprint + um **randomart** determinístico da chave |
|
|
256
|
+
| `/verify <nick>` | Código SAS (~40 bits) + QR + randomart da chave para verificar |
|
|
257
|
+
| `/verify-confirm <nick>` | Marca o peer como verificado |
|
|
258
|
+
| `/backup [caminho]` | Backup cifrado da identidade + peers verificados (restaura no startup) |
|
|
259
|
+
| `/trust <nick>` / `/trustlist` | Aceita chave nova / status de confiança |
|
|
260
|
+
| `/contacts [add\|remove\|all]` | Agenda — apelidos persistentes nos registros de confiança ("esse fingerprint é o João"); aparece no `/users` e viaja no backup de identidade |
|
|
261
|
+
| `/deniable [on\|off]` | Modo de negação plausível |
|
|
261
262
|
| `/lock` / `/autolock <min\|off>` | Trava a tela atrás da passphrase da sessão — na mão ou após inatividade (`autoLock` no config). Privacidade para o "saí um minuto"; o `/panic` é para o pior minuto |
|
|
262
|
-
| `/panic [sim]`
|
|
263
|
-
| `/cover [on\|constant\|off]`
|
|
264
|
-
| `/theme [nome]`
|
|
265
|
-
| `/ephemeral <30s\|5m\|1h\|off>`
|
|
266
|
-
| `/receipts [on\|off]`
|
|
267
|
-
| `/audit [n]`
|
|
263
|
+
| `/panic [sim]` | Wipe de coação — apaga com segurança todos os segredos do disco (sessão, histórico, confiança, chaves) e sai |
|
|
264
|
+
| `/cover [on\|constant\|off]` | Cover traffic — `on` = iscas com jitter, `constant` = canal de taxa constante |
|
|
265
|
+
| `/theme [nome]` | Tema de cores dos nicks: neon, matrix, mono, sunset, ocean |
|
|
266
|
+
| `/ephemeral <30s\|5m\|1h\|off>` | Mensagens autodestrutivas |
|
|
267
|
+
| `/receipts [on\|off]` | Envio de confirmação de leitura (✓✓) |
|
|
268
|
+
| `/audit [n]` | Log de auditoria local |
|
|
268
269
|
|
|
269
270
|
Um **✓** verde ao lado de um nome indica um peer verificado por SAS; um **✗** vermelho sinaliza uma chave que mudou desde a última vez (possível MITM). Um peer novo não-verificado dispara um lembrete único para `/verify`.
|
|
270
271
|
|
|
@@ -273,41 +274,41 @@ Um **✓** verde ao lado de um nome indica um peer verificado por SAS; um **✗*
|
|
|
273
274
|
<details>
|
|
274
275
|
<summary><b>Histórico & arquivos</b></summary>
|
|
275
276
|
|
|
276
|
-
| Comando
|
|
277
|
-
|
|
278
|
-
| `/file <caminho>`
|
|
279
|
-
| `/voice [seg]`
|
|
280
|
-
| `/play [caminho]`
|
|
281
|
-
| `/accept [id]` / `/reject [id]` | Aceita / recusa uma oferta de arquivo recebida
|
|
282
|
-
| `/img [caminho]`
|
|
283
|
-
| `/retention <7d\|24h\|30m>`
|
|
284
|
-
| `/search <termo>`
|
|
285
|
-
| `/find [termo]` — **Ctrl+F**
|
|
286
|
-
| `/doctor [host:porta]`
|
|
287
|
-
| `/history [n]`
|
|
288
|
-
| `/export [caminho]`
|
|
277
|
+
| Comando | Descrição |
|
|
278
|
+
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
279
|
+
| `/file <caminho>` | Oferece arquivo (≤ 50MB) — o destinatário precisa dar `/accept`; retoma |
|
|
280
|
+
| `/voice [seg]` | Grava e envia nota de voz cifrada (precisa de `sox`/`ffmpeg`; default 10s) |
|
|
281
|
+
| `/play [caminho]` | Toca a última nota de voz recebida (`afplay`/`sox`/`ffplay`) |
|
|
282
|
+
| `/accept [id]` / `/reject [id]` | Aceita / recusa uma oferta de arquivo recebida |
|
|
283
|
+
| `/img [caminho]` | Renderiza a última imagem recebida em **alta resolução** (kitty/iTerm2) |
|
|
284
|
+
| `/retention <7d\|24h\|30m>` | Purga o histórico local mais antigo que o tempo dado |
|
|
285
|
+
| `/search <termo>` | Busca no histórico local cifrado (em disco, entre sessões) |
|
|
286
|
+
| `/find [termo]` — **Ctrl+F** | Busca **no histórico da sala na tela** e, com Enter, **salta para a mensagem** destacada |
|
|
287
|
+
| `/doctor [host:porta]` | Diagnostica por que a conexão falha: endereço, DNS, porta TCP, TLS (CA ou self-signed) e versão de protocolo — cada falha com o que fazer |
|
|
288
|
+
| `/history [n]` | Últimas n mensagens do histórico |
|
|
289
|
+
| `/export [caminho]` | Exporta o histórico em .txt ou .json (texto plano!) |
|
|
289
290
|
|
|
290
291
|
</details>
|
|
291
292
|
|
|
292
293
|
<details>
|
|
293
294
|
<summary><b>Presença & diversão</b></summary>
|
|
294
295
|
|
|
295
|
-
| Comando
|
|
296
|
-
|
|
297
|
-
| `/away [motivo]` / `/back`
|
|
298
|
-
| `/mentions [n]`
|
|
299
|
-
| `/status <texto\|off>`
|
|
300
|
-
| `/react <emoji>`
|
|
301
|
-
| `/edit` `/delete`
|
|
302
|
-
| `/pin` `/unpin` `/pins`
|
|
303
|
-
| `/sound` `/notify`
|
|
304
|
-
| `/dnd [on\|off\|mentions\|HH:MM-HH:MM]` | Não perturbe, só menções, ou horário silencioso
|
|
305
|
-
| `/clear`
|
|
296
|
+
| Comando | Descrição |
|
|
297
|
+
| --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
298
|
+
| `/away [motivo]` / `/back` | Marca/remove ausência — enquanto ausente, não-lidas são contadas (`[away · N new]`) e o `/back` mostra um resumo |
|
|
299
|
+
| `/mentions [n]` | Menções recentes a você na sessão (quem, onde, quando) |
|
|
300
|
+
| `/status <texto\|off>` | Status livre — emoji à vontade (`/status :fire: codando`) |
|
|
301
|
+
| `/react <emoji>` | Reage à última mensagem — o emoji aparece **na própria mensagem**, com contagem quando várias pessoas reagem |
|
|
302
|
+
| `/edit` `/delete` | Edita ou apaga sua última mensagem — a **linha original é reescrita no lugar** (marcada _(edited)_) ou vira uma lápide, em vez de uma linha nova que você precisa juntar mentalmente à original |
|
|
303
|
+
| `/pin` `/unpin` `/pins` | Fixa mensagens |
|
|
304
|
+
| `/sound` `/notify` | Notificações sonoras / desktop |
|
|
305
|
+
| `/dnd [on\|off\|mentions\|HH:MM-HH:MM]` | Não perturbe, só menções, ou horário silencioso |
|
|
306
|
+
| `/clear` | Limpa o chat |
|
|
306
307
|
|
|
307
308
|
</details>
|
|
308
309
|
|
|
309
310
|
Digitar `:fire:` em qualquer lugar vira 🔥 (Tab autocompleta shortcodes).
|
|
310
|
-
**Ctrl+K** abre uma paleta de comandos fuzzy, **Ctrl+E** um seletor de emoji. PageUp/PageDown rolam o histórico. **Alt+Enter** (ou Shift+Enter onde o terminal suporta, além de Ctrl+J) insere uma nova linha para mensagens de várias linhas; Enter envia. Colar texto multi-linha (código incluso) preserva as quebras — cola, confere, Enter. Markdown funciona: \`código\`, **negrito**,
|
|
311
|
+
**Ctrl+K** abre uma paleta de comandos fuzzy, **Ctrl+E** um seletor de emoji. PageUp/PageDown rolam o histórico. **Alt+Enter** (ou Shift+Enter onde o terminal suporta, além de Ctrl+J) insere uma nova linha para mensagens de várias linhas; Enter envia. Colar texto multi-linha (código incluso) preserva as quebras — cola, confere, Enter. Markdown funciona: \`código\`, **negrito**, _itálico_, links, além de blocos de código \`\`\` e | tabelas |. Imagens recebidas têm preview inline (half-blocks) e renderizam em alta resolução com `/img` no kitty/iTerm2. Separadores de dia e agrupamento de mensagens deixam o log limpo.
|
|
311
312
|
|
|
312
313
|
### Primeira execução & arquivo de config
|
|
313
314
|
|
|
@@ -327,7 +328,7 @@ mão. Todas as chaves são opcionais (chaves desconhecidas são ignoradas):
|
|
|
327
328
|
|
|
328
329
|
```json
|
|
329
330
|
{
|
|
330
|
-
"nickname": "
|
|
331
|
+
"nickname": "ana",
|
|
331
332
|
"server": "wss://100.x.y.z:3600",
|
|
332
333
|
"sound": false,
|
|
333
334
|
"notify": true,
|
|
@@ -364,7 +365,7 @@ mão. Todas as chaves são opcionais (chaves desconhecidas são ignoradas):
|
|
|
364
365
|
**Argon2id + XSalsa20-Poly1305** — sem passphrase, nada persiste.
|
|
365
366
|
- **Pós-quântico híbrido**: cada sessão mistura um segredo ML-KEM-768 na raiz
|
|
366
367
|
do ratchet na inicialização, então tráfego gravado hoje continua ilegível
|
|
367
|
-
para um adversário quântico futuro. Ele é
|
|
368
|
+
para um adversário quântico futuro. Ele é _somado_ ao X25519, nunca o
|
|
368
369
|
substitui — a segurança é no mínimo a clássica. O `/trustlist` mostra `[PQ]`.
|
|
369
370
|
- **Salas privadas** nunca enviam a senha a lugar nenhum: ela deriva uma chave
|
|
370
371
|
Ed25519 (Argon2id) que responde a um desafio do servidor, e o conteúdo da
|
package/docs/PLUGINS.md
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
# CipherMesh Plugin API
|
|
2
2
|
|
|
3
|
-
CipherMesh loads user plugins
|
|
4
|
-
|
|
3
|
+
CipherMesh loads user plugins from `~/.ciphermesh/plugins/*.js` and routes
|
|
4
|
+
unknown slash-commands to them. Plugins work in both relay and P2P mode.
|
|
5
|
+
|
|
6
|
+
**Nothing there runs until you say so.** A file in that directory is found, not
|
|
7
|
+
loaded. Run `/plugins` to see what is waiting and `/plugins allow <file>` to
|
|
8
|
+
approve it — the approval is remembered in `~/.ciphermesh/config.json` under
|
|
9
|
+
`pluginsAllowed`, so you are asked once per file.
|
|
5
10
|
|
|
6
11
|
## Quick start
|
|
7
12
|
|
|
@@ -10,8 +15,15 @@ mkdir -p ~/.ciphermesh/plugins
|
|
|
10
15
|
cp examples/plugins/roll.js examples/plugins/poll.js ~/.ciphermesh/plugins/
|
|
11
16
|
```
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
Then in the client:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
/plugins → shows roll.js and poll.js waiting
|
|
22
|
+
/plugins allow roll → approved, loaded, and remembered
|
|
23
|
+
/plugins allow poll
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Now `/roll 2d20+3` and `/poll Pizza tonight? | yes | obviously` work.
|
|
15
27
|
|
|
16
28
|
## Plugin format
|
|
17
29
|
|
|
@@ -19,8 +31,8 @@ A plugin is an ES module whose **default export** is:
|
|
|
19
31
|
|
|
20
32
|
```js
|
|
21
33
|
export default {
|
|
22
|
-
name: 'roll',
|
|
23
|
-
description: 'Roll dice',
|
|
34
|
+
name: 'roll', // required, unique
|
|
35
|
+
description: 'Roll dice', // optional, shown by /plugins
|
|
24
36
|
commands: {
|
|
25
37
|
// key = command name (with or without the leading slash)
|
|
26
38
|
roll(args) {
|
|
@@ -31,16 +43,18 @@ export default {
|
|
|
31
43
|
};
|
|
32
44
|
```
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
A file that fails to import, or lacks `name`/`commands`, is skipped — but if you
|
|
47
|
+
approved it, `/plugins` says so. A plugin you asked for and did not get should
|
|
48
|
+
not disappear without a word.
|
|
35
49
|
|
|
36
50
|
## Handler return values
|
|
37
51
|
|
|
38
|
-
| Return
|
|
39
|
-
|
|
40
|
-
| `{ send: '<text>' }`
|
|
41
|
-
| `{ info: '<text>' }`
|
|
42
|
-
| `'<text>'` (plain string)
|
|
43
|
-
| `null` / `undefined` / throw | Treated as "not handled": the user sees
|
|
52
|
+
| Return | Effect |
|
|
53
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
54
|
+
| `{ send: '<text>' }` | The text is **sent to the current room** as a normal end-to-end-encrypted message (and echoed locally). Markdown and multi-line text work. |
|
|
55
|
+
| `{ info: '<text>' }` | Shown **only locally** as an info line. |
|
|
56
|
+
| `'<text>'` (plain string) | Same as `{ info }` — the original API, still supported. |
|
|
57
|
+
| `null` / `undefined` / throw | Treated as "not handled": the user sees _Unknown command_. |
|
|
44
58
|
|
|
45
59
|
Handlers are synchronous — return the final value directly.
|
|
46
60
|
|
|
@@ -54,13 +68,38 @@ directory listing).
|
|
|
54
68
|
## Security model — read this
|
|
55
69
|
|
|
56
70
|
A plugin is **arbitrary JavaScript running inside your chat process**, with
|
|
57
|
-
your privileges and full access to your keys in memory. There is no sandbox
|
|
71
|
+
your privileges and full access to your keys in memory. There is no sandbox,
|
|
72
|
+
and approving one does not create one.
|
|
58
73
|
|
|
59
74
|
- Only install plugins you wrote or read line-by-line.
|
|
60
75
|
- Treat a plugin file like you treat `curl | sh`.
|
|
61
76
|
- Plugins are never synced, auto-updated or downloaded by CipherMesh — the
|
|
62
77
|
only way code gets into `~/.ciphermesh/plugins/` is you putting it there.
|
|
63
78
|
|
|
79
|
+
### What approval actually buys
|
|
80
|
+
|
|
81
|
+
Before, any `.js` file appearing in that directory ran at the next start. The
|
|
82
|
+
warning above protected only the people who had already read it, and anything
|
|
83
|
+
able to write one file into a known path had code execution.
|
|
84
|
+
|
|
85
|
+
Now the file is listed and left alone until you approve it. That is the whole
|
|
86
|
+
guarantee, and it is worth being precise about its edges:
|
|
87
|
+
|
|
88
|
+
- **It is a consent step, not a sandbox.** An approved plugin can do everything
|
|
89
|
+
the client can do. Approve for the same reasons you would run a script.
|
|
90
|
+
- **Approval is per _file name_, not per plugin name.** That is forced, not
|
|
91
|
+
chosen: a plugin's own `name` lives inside the module, and reading it means
|
|
92
|
+
importing the module, and importing it is already running it. The check has
|
|
93
|
+
to work from the directory listing alone.
|
|
94
|
+
- **Replacing an approved file is not a new decision.** `roll.js` stays
|
|
95
|
+
approved even if its contents change completely. If you did not put the new
|
|
96
|
+
contents there, you have a bigger problem than plugins — but do not read the
|
|
97
|
+
approval as a promise about what the file contains.
|
|
98
|
+
|
|
99
|
+
There is no capability system, deliberately. Declaring what a plugin may do
|
|
100
|
+
without being able to enforce it would make the risk look bounded when it is
|
|
101
|
+
not, which is worse than the plain warning above.
|
|
102
|
+
|
|
64
103
|
## Included examples
|
|
65
104
|
|
|
66
105
|
- [`examples/plugins/roll.js`](../examples/plugins/roll.js) — dice roller,
|