beeperbox 0.7.0 → 0.8.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/README.md +2 -1
- package/package.json +1 -1
- package/server.js +20 -6
package/README.md
CHANGED
|
@@ -35,10 +35,11 @@ BEEPER_TOKEN=your-token-here npx beeperbox --stdio
|
|
|
35
35
|
| `MCP_PORT` | MCP HTTP port | `23375` |
|
|
36
36
|
| `MCP_AUTH_TOKEN` | Optional bearer guard on the MCP endpoint | unset (open on loopback) |
|
|
37
37
|
| `MCP_ALLOWED_HOSTS` | Host/Origin allowlist | `localhost,127.0.0.1,::1` |
|
|
38
|
+
| `MCP_BIND_ADDR` | Interface the MCP server binds | `127.0.0.1` (loopback) |
|
|
38
39
|
|
|
39
40
|
## Security
|
|
40
41
|
|
|
41
|
-
The server binds `
|
|
42
|
+
The server binds **loopback only** (`127.0.0.1`) by default, so it's safe with no auth — only processes on your own machine can reach it. Don't just set it to `0.0.0.0`: a same-network attacker can spoof the `Host` header past the allowlist and reach the full tool surface (read every message, send across every network) unauthenticated. To expose it deliberately, set `MCP_BIND_ADDR=0.0.0.0` **and** `MCP_AUTH_TOKEN`, and put it behind a tunnel (SSH / Tailscale / TLS reverse proxy) — never raw on a public interface.
|
|
42
43
|
|
|
43
44
|
## Supervision
|
|
44
45
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeperbox",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Lite mode for beeperbox — the opinionated MCP verb server for Beeper Desktop, run standalone against a Beeper you already have open (no Docker, no Electron). The full headless-Beeper-in-Docker build lives at github.com/hamr0/beeperbox.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"beeperbox": "server.js"
|
package/server.js
CHANGED
|
@@ -22,6 +22,18 @@ const PORT = parseInt(process.env.MCP_PORT || '23375', 10);
|
|
|
22
22
|
const BEEPER_API = process.env.BEEPER_API || 'http://127.0.0.1:23373';
|
|
23
23
|
const BEEPER_TOKEN = process.env.BEEPER_TOKEN || '';
|
|
24
24
|
|
|
25
|
+
// Bind address. Defaults to LOOPBACK (127.0.0.1) — the safe default for lite
|
|
26
|
+
// mode (`npx beeperbox` on a laptop), where the process listens directly on the
|
|
27
|
+
// host with no Docker loopback publish in front of it. Binding 0.0.0.0 there
|
|
28
|
+
// would put the full tool surface (read every message, send across every
|
|
29
|
+
// network) on the LAN, reachable unauthenticated by anyone who can spoof the
|
|
30
|
+
// `Host` header — a non-browser attacker trivially can. The container needs
|
|
31
|
+
// 0.0.0.0 (a Docker published port can't reach a loopback-bound process) and
|
|
32
|
+
// sets MCP_BIND_ADDR=0.0.0.0 via the image ENV; there the loopback *publish*
|
|
33
|
+
// (`127.0.0.1:23375:23375`), not the bind, is the boundary. To expose lite mode
|
|
34
|
+
// deliberately, set MCP_BIND_ADDR=0.0.0.0 AND MCP_AUTH_TOKEN AND a tunnel.
|
|
35
|
+
const BIND_ADDR = process.env.MCP_BIND_ADDR || '127.0.0.1';
|
|
36
|
+
|
|
25
37
|
// ─── http transport hardening ─────────────────────────────────────
|
|
26
38
|
// The HTTP transport is the network-exposed surface (stdio is local-only).
|
|
27
39
|
// Three guards, all configurable so they don't break the documented
|
|
@@ -34,10 +46,11 @@ const BEEPER_TOKEN = process.env.BEEPER_TOKEN || '';
|
|
|
34
46
|
// Defaults to loopback; set it for reverse proxies.
|
|
35
47
|
// MCP_MAX_BODY — max request body bytes (default 1 MiB) so a large
|
|
36
48
|
// POST can't grow the in-memory buffer unbounded.
|
|
37
|
-
// The
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
49
|
+
// The bind address is MCP_BIND_ADDR (see above): loopback by default (safe for
|
|
50
|
+
// lite mode), 0.0.0.0 in the container (where the loopback PUBLISH is the
|
|
51
|
+
// boundary). In the container, Auth + Host/Origin are the in-container defense;
|
|
52
|
+
// in lite mode the loopback BIND is, because a non-browser client can spoof the
|
|
53
|
+
// Host header past the allowlist.
|
|
41
54
|
const MCP_AUTH_TOKEN = process.env.MCP_AUTH_TOKEN || '';
|
|
42
55
|
const MCP_ALLOWED_HOSTS = new Set(
|
|
43
56
|
(process.env.MCP_ALLOWED_HOSTS || 'localhost,127.0.0.1,::1,[::1]')
|
|
@@ -1217,8 +1230,8 @@ function startHttpTransport() {
|
|
|
1217
1230
|
});
|
|
1218
1231
|
});
|
|
1219
1232
|
|
|
1220
|
-
server.listen(PORT,
|
|
1221
|
-
console.log(`[beeperbox-mcp] listening on http
|
|
1233
|
+
server.listen(PORT, BIND_ADDR, () => {
|
|
1234
|
+
console.log(`[beeperbox-mcp] listening on http://${BIND_ADDR}:${PORT}${BIND_ADDR === '0.0.0.0' ? ' (all interfaces — rely on a loopback publish or set MCP_AUTH_TOKEN)' : ' (loopback only)'}`);
|
|
1222
1235
|
console.log(`[beeperbox-mcp] beeper api: ${BEEPER_API}`);
|
|
1223
1236
|
console.log(`[beeperbox-mcp] beeper token: ${BEEPER_TOKEN ? 'set' : 'NOT SET (set BEEPER_TOKEN env var)'}`);
|
|
1224
1237
|
console.log(`[beeperbox-mcp] http auth: ${MCP_AUTH_TOKEN ? 'required (MCP_AUTH_TOKEN set)' : 'OPEN — set MCP_AUTH_TOKEN to require a bearer token'}`);
|
|
@@ -1288,6 +1301,7 @@ module.exports = {
|
|
|
1288
1301
|
// version + tool names here is what guarantees the two builds can't drift.
|
|
1289
1302
|
VERSION,
|
|
1290
1303
|
TOOL_NAMES: TOOLS.map((t) => t.name),
|
|
1304
|
+
BIND_ADDR,
|
|
1291
1305
|
ledgerPath,
|
|
1292
1306
|
encodeCursor,
|
|
1293
1307
|
decodeCursor,
|