ciphermesh 2.9.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 CHANGED
@@ -3,6 +3,47 @@
3
3
  Notable changes per release. Older versions are reconstructed from the git
4
4
  history — the commit bodies and pull requests remain the fuller record.
5
5
 
6
+ ## 2.10.0
7
+
8
+ ### Added
9
+
10
+ - **\`--check\` for operators.** The deploy guide already explained every
11
+ footgun here, which is the problem: a document is read once, by whoever set
12
+ the machine up, while the misconfiguration lasts as long as the machine does.
13
+ The worst of them — \`TRUST_PROXY\` left off behind a reverse proxy — is
14
+ invisible from outside, because everything works and the per-IP cap, the rate
15
+ limit and the banlist simply apply to the proxy and protect nobody.
16
+
17
+ \`ciphermesh-server --check\` validates and exits without opening a socket, so
18
+ it is safe against a live host, and exits non-zero on an error so a deploy
19
+ script can gate on it. The same findings print at every startup, because a
20
+ warning you have to ask for is a warning nobody sees.
21
+
22
+ - **Connection-rate limiting.** The relay capped how many sockets one address
23
+ could hold, and how many messages a session could send, but nothing capped how
24
+ fast an address could _open_ connections. Connect, run the hybrid handshake,
25
+ disconnect, repeat: the concurrency cap never trips because the sockets are
26
+ never held, and every attempt costs the relay an X25519 and an ML-KEM-768
27
+ operation while costing the client almost nothing. That asymmetry was the one
28
+ real denial-of-service route into a public hub.
29
+
30
+ An address that exceeds `CONNECTION_RATE_PER_MINUTE` (60 by default, still
31
+ LAN-friendly) is refused for a minute, then five, then thirty. An hour of
32
+ behaving clears the record, so a shared NAT gateway cannot accumulate strikes
33
+ forever, and the refusal says how long to wait so a well-behaved client backs
34
+ off instead of extending its own ban.
35
+
36
+ - **A byte budget per connection** (`MAX_BYTES_PER_SECOND`,
37
+ `MAX_BYTES_BURST`). The message limit counts messages, and messages are padded
38
+ into buckets of up to 32 KiB, so a session sitting at the limit is a
39
+ multi-megabit stream. Bytes are the resource that runs out. The burst
40
+ allowance keeps a legitimate file transfer from looking like an attack.
41
+
42
+ Both are continuously refilling token buckets rather than fixed windows: a
43
+ fixed window lets a caller spend its whole allowance at the end of one window
44
+ and again at the start of the next, which is twice the intended rate at
45
+ exactly the moment an attacker aims for.
46
+
6
47
  ## 2.9.0
7
48
 
8
49
  ### Added
package/README.md CHANGED
@@ -204,10 +204,11 @@ software was built for.
204
204
  | ----------------------------- | ----------------------------------------------------------------- |
205
205
  | `/help` | All commands |
206
206
  | `/tips` | Show a rotating security/UX tip |
207
+ | `/plugins` | List loaded plugins and the commands they add |
207
208
  | `/users` | Who's online (with away/status) |
208
209
  | `/msg <nick> <text>` | Private message (DM) |
209
210
  | `/reply <text>` | Reply quoting the last received message |
210
- | `/me <action>` | Third-person action — _«felipe is compiling»_ |
211
+ | `/me <action>` | Third-person action — _«ana is compiling»_ |
211
212
  | `/watch [add\|remove\|clear]` | Alert on a keyword in **any** room, like a mention |
212
213
  | `/invite [host:port]` | Generate a `ciphermesh://` invite + QR code |
213
214
  | `/nick <new>` | Change nickname (before joining — recovers from "nickname taken") |
@@ -294,6 +295,7 @@ A green **✓** next to a name marks a SAS-verified peer; a red **✗** flags a
294
295
  | Command | Description |
295
296
  | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
296
297
  | `/away [reason]` / `/back` | Mark yourself away — while away, unreads are counted (`[away · N new]`) and `/back` shows a summary |
298
+ | `/autoaway <minutes\|off>` | Go away by itself after that long without typing (1–240 minutes) |
297
299
  | `/mentions [n]` | Recent mentions of you this session (who, where, when) |
298
300
  | `/status <text\|off>` | Free-form status — emojis welcome (`/status :fire: coding`) |
299
301
  | `/react <emoji>` | React to the last message — the emoji lands **on the message**, with a count when several people react |
@@ -325,7 +327,7 @@ All keys are optional (unknown keys are ignored):
325
327
 
326
328
  ```json
327
329
  {
328
- "nickname": "felipe",
330
+ "nickname": "ana",
329
331
  "server": "wss://100.x.y.z:3600",
330
332
  "sound": false,
331
333
  "notify": true,
package/README.pt-BR.md CHANGED
@@ -209,7 +209,7 @@ aquela para a qual este software foi feito.
209
209
  | `/users` | Quem está online (com away/status) |
210
210
  | `/msg <nick> <texto>` | Mensagem privada (DM) |
211
211
  | `/reply <texto>` | Responde citando a última mensagem recebida |
212
- | `/me <ação>` | Ação em terceira pessoa — _«felipe está compilando»_ |
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
214
  | `/invite [host:porta]` | Gera convite `ciphermesh://` + QR code |
215
215
  | `/nick <novo>` | Troca de apelido (antes de entrar — recupera de "apelido em uso") |
@@ -328,7 +328,7 @@ mão. Todas as chaves são opcionais (chaves desconhecidas são ignoradas):
328
328
 
329
329
  ```json
330
330
  {
331
- "nickname": "felipe",
331
+ "nickname": "ana",
332
332
  "server": "wss://100.x.y.z:3600",
333
333
  "sound": false,
334
334
  "notify": true,
package/docs/PLUGINS.md CHANGED
@@ -1,7 +1,12 @@
1
1
  # CipherMesh Plugin API
2
2
 
3
- CipherMesh loads user plugins at startup from `~/.ciphermesh/plugins/*.js` and
4
- routes unknown slash-commands to them. Plugins work in both relay and P2P mode.
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
- Restart the client — `/plugins` lists what loaded, and `/roll 2d20+3` /
14
- `/poll Pizza tonight? | yes | obviously` just work.
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', // required, unique
23
- description: 'Roll dice', // optional, shown by /plugins
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
- Files that fail to import, or lack `name`/`commands`, are skipped silently.
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 | Effect |
39
- |--------|--------|
40
- | `{ 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. |
41
- | `{ info: '<text>' }` | Shown **only locally** as an info line. |
42
- | `'<text>'` (plain string) | Same as `{ info }` — the original API, still supported. |
43
- | `null` / `undefined` / throw | Treated as "not handled": the user sees *Unknown command*. |
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,