@vex-chat/spire 1.0.0 → 1.0.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 (66) hide show
  1. package/README.md +38 -38
  2. package/dist/ClientManager.d.ts +1 -3
  3. package/dist/ClientManager.js +7 -50
  4. package/dist/ClientManager.js.map +1 -1
  5. package/dist/Database.d.ts +49 -6
  6. package/dist/Database.js +255 -80
  7. package/dist/Database.js.map +1 -1
  8. package/dist/Spire.d.ts +0 -3
  9. package/dist/Spire.js +116 -81
  10. package/dist/Spire.js.map +1 -1
  11. package/dist/db/schema.d.ts +1 -0
  12. package/dist/migrations/2026-04-14_argon2id-password-hashing.d.ts +3 -0
  13. package/dist/migrations/2026-04-14_argon2id-password-hashing.js +12 -0
  14. package/dist/migrations/2026-04-14_argon2id-password-hashing.js.map +1 -0
  15. package/dist/run.js +0 -1
  16. package/dist/run.js.map +1 -1
  17. package/dist/server/avatar.d.ts +1 -3
  18. package/dist/server/avatar.js +7 -11
  19. package/dist/server/avatar.js.map +1 -1
  20. package/dist/server/errors.d.ts +3 -6
  21. package/dist/server/errors.js +2 -28
  22. package/dist/server/errors.js.map +1 -1
  23. package/dist/server/file.d.ts +1 -2
  24. package/dist/server/file.js +5 -7
  25. package/dist/server/file.js.map +1 -1
  26. package/dist/server/index.d.ts +1 -2
  27. package/dist/server/index.js +35 -33
  28. package/dist/server/index.js.map +1 -1
  29. package/dist/server/invite.d.ts +1 -2
  30. package/dist/server/invite.js +1 -1
  31. package/dist/server/invite.js.map +1 -1
  32. package/dist/server/rateLimit.d.ts +13 -3
  33. package/dist/server/rateLimit.js +57 -6
  34. package/dist/server/rateLimit.js.map +1 -1
  35. package/dist/server/user.d.ts +1 -2
  36. package/dist/server/user.js +10 -8
  37. package/dist/server/user.js.map +1 -1
  38. package/dist/utils/jwtSecret.d.ts +3 -3
  39. package/dist/utils/jwtSecret.js +5 -6
  40. package/dist/utils/jwtSecret.js.map +1 -1
  41. package/dist/utils/loadEnv.js +1 -1
  42. package/dist/utils/loadEnv.js.map +1 -1
  43. package/package.json +17 -8
  44. package/src/ClientManager.ts +7 -60
  45. package/src/Database.ts +308 -89
  46. package/src/Spire.ts +122 -119
  47. package/src/__tests__/Database.spec.ts +0 -17
  48. package/src/db/schema.ts +1 -0
  49. package/src/migrations/2026-04-14_argon2id-password-hashing.ts +18 -0
  50. package/src/run.ts +0 -1
  51. package/src/server/avatar.ts +7 -14
  52. package/src/server/errors.ts +3 -32
  53. package/src/server/file.ts +5 -10
  54. package/src/server/index.ts +37 -37
  55. package/src/server/invite.ts +0 -2
  56. package/src/server/rateLimit.ts +43 -9
  57. package/src/server/user.ts +9 -9
  58. package/src/utils/jwtSecret.ts +5 -6
  59. package/src/utils/loadEnv.ts +1 -1
  60. package/dist/__tests__/Database.spec.d.ts +0 -1
  61. package/dist/__tests__/Database.spec.js +0 -136
  62. package/dist/__tests__/Database.spec.js.map +0 -1
  63. package/dist/utils/createLogger.d.ts +0 -5
  64. package/dist/utils/createLogger.js +0 -35
  65. package/dist/utils/createLogger.js.map +0 -1
  66. package/src/utils/createLogger.ts +0 -47
@@ -1,4 +1,52 @@
1
+ import { timingSafeEqual } from "node:crypto";
1
2
  import rateLimit, { ipKeyGenerator } from "express-rate-limit";
3
+ /** HTTP header carrying the dev API key (must match {@link process.env.DEV_API_KEY}). */
4
+ export const DEV_API_KEY_HEADER = "x-dev-api-key";
5
+ /**
6
+ * When `DEV_API_KEY` is set in the environment, any request whose
7
+ * `x-dev-api-key` header matches (constant-time) skips all in-process rate
8
+ * limiters. Dev / load-testing escape hatch only — never set in production.
9
+ * (Future: first-class API keys with scopes may reuse this header name.)
10
+ */
11
+ export function devApiKeySkipsRateLimits(req) {
12
+ const configured = process.env["DEV_API_KEY"]?.trim() ?? "";
13
+ if (configured.length === 0) {
14
+ return false;
15
+ }
16
+ const presented = req.get(DEV_API_KEY_HEADER);
17
+ if (!presented || presented.length !== configured.length) {
18
+ return false;
19
+ }
20
+ try {
21
+ return timingSafeEqual(Buffer.from(presented, "utf8"), Buffer.from(configured, "utf8"));
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ /**
28
+ * Rate limiting middleware.
29
+ *
30
+ * Three tiers matching CWE-307 (brute-force auth), CWE-400 / CWE-770
31
+ * (unrestricted resource consumption), and OWASP API4:2023:
32
+ *
33
+ * - `globalLimiter` — baseline per-IP limit across every route. Wide
34
+ * enough to not bother normal clients, tight enough to shield the
35
+ * server from a single-host flood.
36
+ * - `authLimiter` — strict per-IP limit on auth endpoints (register,
37
+ * login, device challenge). `skipSuccessfulRequests` means only the
38
+ * failed attempts count, so a correct login doesn't eat the budget.
39
+ * - `uploadLimiter` — upload-specific limit applied before multer,
40
+ * so multer never even parses a request that's over quota.
41
+ *
42
+ * All three use `ipKeyGenerator` from `express-rate-limit@7.4+` to
43
+ * bucket IPv4 and IPv4-mapped IPv6 correctly (CVE-2026-30827 — older
44
+ * versions silently collapsed all IPv4-mapped IPv6 addresses into
45
+ * one bucket, which let attackers bypass the limiter).
46
+ *
47
+ * `trust proxy` must be set on the Express app (see Spire.ts) so
48
+ * `req.ip` returns the real client address, not the immediate proxy.
49
+ */
2
50
  /**
3
51
  * Bucket requests by the real client IP, IPv6-safe.
4
52
  *
@@ -11,14 +59,15 @@ const keyByIp = (req) => ipKeyGenerator(req.ip ?? "");
11
59
  /**
12
60
  * Global per-IP limiter. Applied app-wide via `api.use(globalLimiter)`.
13
61
  *
14
- * 300 requests per 15 minutes per client IP. A human chatting via a
62
+ * 3000 requests per 15 minutes per client IP. A human chatting via a
15
63
  * browser or the libvex client won't come close; a single-host DoS
16
64
  * gets throttled quickly.
17
65
  */
18
66
  export const globalLimiter = rateLimit({
19
67
  keyGenerator: keyByIp,
20
68
  legacyHeaders: false,
21
- limit: 300,
69
+ limit: 3000,
70
+ skip: devApiKeySkipsRateLimits,
22
71
  standardHeaders: "draft-7",
23
72
  windowMs: 15 * 60 * 1000,
24
73
  });
@@ -26,7 +75,7 @@ export const globalLimiter = rateLimit({
26
75
  * Strict auth endpoint limiter. Applied per-route to /auth, /register,
27
76
  * and /auth/device.
28
77
  *
29
- * 5 failed attempts per 15 minutes per IP. Successful logins don't
78
+ * 50 failed attempts per 15 minutes per IP. Successful logins don't
30
79
  * count (`skipSuccessfulRequests`), so a normal user doesn't lock
31
80
  * themselves out by fat-fingering a password once. Blocks brute force
32
81
  * (CWE-307) without harming UX.
@@ -34,7 +83,8 @@ export const globalLimiter = rateLimit({
34
83
  export const authLimiter = rateLimit({
35
84
  keyGenerator: keyByIp,
36
85
  legacyHeaders: false,
37
- limit: 5,
86
+ limit: 50,
87
+ skip: devApiKeySkipsRateLimits,
38
88
  skipSuccessfulRequests: true,
39
89
  standardHeaders: "draft-7",
40
90
  windowMs: 15 * 60 * 1000,
@@ -45,13 +95,14 @@ export const authLimiter = rateLimit({
45
95
  * upload attempts per minute so an attacker can't force spire to
46
96
  * spend CPU/IO on repeated large-body parses.
47
97
  *
48
- * 20 uploads per minute per IP — generous for a chat client (rapid-
98
+ * 200 uploads per minute per IP — generous for a chat client (rapid-
49
99
  * fire image attachments) but tight enough to shield the disk.
50
100
  */
51
101
  export const uploadLimiter = rateLimit({
52
102
  keyGenerator: keyByIp,
53
103
  legacyHeaders: false,
54
- limit: 20,
104
+ limit: 200,
105
+ skip: devApiKeySkipsRateLimits,
55
106
  standardHeaders: "draft-7",
56
107
  windowMs: 60 * 1000,
57
108
  });
@@ -1 +1 @@
1
- {"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/server/rateLimit.ts"],"names":[],"mappings":"AAyBA,OAAO,SAAS,EAAE,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;IACnC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,GAAG;IACV,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC3B,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,CAAC;IACR,sBAAsB,EAAE,IAAI;IAC5B,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC3B,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;IACnC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,EAAE;IACT,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,IAAI;CACtB,CAAC,CAAC"}
1
+ {"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/server/rateLimit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,SAAS,EAAE,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE/D,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC;AAElD;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAY;IACjD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC5D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IAC9C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;QACvD,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,IAAI,CAAC;QACD,OAAO,eAAe,CAClB,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,EAC9B,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAClC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,KAAK,CAAC;IACjB,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;GAOG;AACH,MAAM,OAAO,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;IACnC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,IAAI;IACX,IAAI,EAAE,wBAAwB;IAC9B,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC3B,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,SAAS,CAAC;IACjC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,EAAE;IACT,IAAI,EAAE,wBAAwB;IAC9B,sBAAsB,EAAE,IAAI;IAC5B,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC3B,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,SAAS,CAAC;IACnC,YAAY,EAAE,OAAO;IACrB,aAAa,EAAE,KAAK;IACpB,KAAK,EAAE,GAAG;IACV,IAAI,EAAE,wBAAwB;IAC9B,eAAe,EAAE,SAAS;IAC1B,QAAQ,EAAE,EAAE,GAAG,IAAI;CACtB,CAAC,CAAC"}
@@ -1,4 +1,3 @@
1
1
  import type { Database } from "../Database.ts";
2
- import type winston from "winston";
3
2
  import { TokenScopes } from "@vex-chat/types";
4
- export declare const getUserRouter: (db: Database, log: winston.Logger, tokenValidator: (key: string, scope: TokenScopes) => boolean) => import("express-serve-static-core").Router;
3
+ export declare const getUserRouter: (db: Database, tokenValidator: (key: string, scope: TokenScopes) => boolean) => import("express-serve-static-core").Router;
@@ -6,7 +6,7 @@ import { stringify } from "uuid";
6
6
  import { msgpack } from "../utils/msgpack.js";
7
7
  import { censorUser, getParam, getUser } from "./utils.js";
8
8
  import { protect } from "./index.js";
9
- export const getUserRouter = (db, log, tokenValidator) => {
9
+ export const getUserRouter = (db, tokenValidator) => {
10
10
  const router = express.Router();
11
11
  router.get("/:id", protect, async (req, res) => {
12
12
  const user = await db.retrieveUser(getParam(req, "id"));
@@ -18,9 +18,13 @@ export const getUserRouter = (db, log, tokenValidator) => {
18
18
  }
19
19
  });
20
20
  router.get("/:id/devices", protect, async (req, res) => {
21
- const deviceList = await db.retrieveUserDeviceList([
22
- getParam(req, "id"),
23
- ]);
21
+ const id = getParam(req, "id");
22
+ const user = await db.retrieveUser(id);
23
+ if (!user) {
24
+ res.sendStatus(404);
25
+ return;
26
+ }
27
+ const deviceList = await db.retrieveUserDeviceList([id]);
24
28
  return res.send(msgpack.encode(deviceList));
25
29
  });
26
30
  router.get("/:id/permissions", protect, async (req, res) => {
@@ -69,7 +73,6 @@ export const getUserRouter = (db, log, tokenValidator) => {
69
73
  const deviceData = parsed.data;
70
74
  const token = xSignOpen(XUtils.decodeHex(deviceData.signed), XUtils.decodeHex(deviceData.signKey));
71
75
  if (!token) {
72
- log.warn("Invalid signature on token.");
73
76
  res.sendStatus(400);
74
77
  return;
75
78
  }
@@ -78,9 +81,8 @@ export const getUserRouter = (db, log, tokenValidator) => {
78
81
  const device = await db.createDevice(userDetails.userID, deviceData);
79
82
  res.send(msgpack.encode(device));
80
83
  }
81
- catch (err) {
82
- log.warn(String(err));
83
- // failed registration due to signkey being taken
84
+ catch (_err) {
85
+ // signkey already taken
84
86
  res.sendStatus(470);
85
87
  return;
86
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/server/user.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/server/user.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Returns the JWT signing secret.
2
+ * Returns the dedicated JWT HMAC signing secret.
3
3
  *
4
- * Prefers JWT_SECRET (dedicated HMAC key) over SPK (NaCl server signing key).
5
- * Falls back to SPK for backward compat with existing deployments.
4
+ * This MUST be a separate key from SPK (the Ed25519 server signing key)
5
+ * so that compromise of one does not affect the other.
6
6
  */
7
7
  export declare function getJwtSecret(): string;
@@ -1,14 +1,13 @@
1
1
  /**
2
- * Returns the JWT signing secret.
2
+ * Returns the dedicated JWT HMAC signing secret.
3
3
  *
4
- * Prefers JWT_SECRET (dedicated HMAC key) over SPK (NaCl server signing key).
5
- * Falls back to SPK for backward compat with existing deployments.
4
+ * This MUST be a separate key from SPK (the Ed25519 server signing key)
5
+ * so that compromise of one does not affect the other.
6
6
  */
7
7
  export function getJwtSecret() {
8
- const secret = process.env["JWT_SECRET"] ?? process.env["SPK"];
8
+ const secret = process.env["JWT_SECRET"];
9
9
  if (!secret) {
10
- throw new Error("Neither JWT_SECRET nor SPK is set. " +
11
- "Set JWT_SECRET (preferred) or SPK in your environment.");
10
+ throw new Error("JWT_SECRET is not set. Generate one with: node scripts/gen-spk.js");
12
11
  }
13
12
  return secret;
14
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"jwtSecret.js","sourceRoot":"","sources":["../../src/utils/jwtSecret.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACX,qCAAqC;YACjC,wDAAwD,CAC/D,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
1
+ {"version":3,"file":"jwtSecret.js","sourceRoot":"","sources":["../../src/utils/jwtSecret.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,YAAY;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzC,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACX,mEAAmE,CACtE,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -2,7 +2,7 @@ import { config } from "dotenv";
2
2
  /* Populate process.env with vars from .env and verify required vars are present. */
3
3
  export function loadEnv() {
4
4
  config();
5
- const requiredEnvVars = ["CANARY", "DB_TYPE", "SPK"];
5
+ const requiredEnvVars = ["DB_TYPE", "JWT_SECRET", "SPK"];
6
6
  for (const required of requiredEnvVars) {
7
7
  if (process.env[required] === undefined) {
8
8
  process.stderr.write(`Required environment variable '${required}' is not set. Please consult the README.\n`);
@@ -1 +1 @@
1
- {"version":3,"file":"loadEnv.js","sourceRoot":"","sources":["../../src/utils/loadEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,oFAAoF;AACpF,MAAM,UAAU,OAAO;IACnB,MAAM,EAAE,CAAC;IACT,MAAM,eAAe,GAAa,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC/D,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,kCAAkC,QAAQ,4CAA4C,CACzF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"loadEnv.js","sourceRoot":"","sources":["../../src/utils/loadEnv.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,oFAAoF;AACpF,MAAM,UAAU,OAAO;IACnB,MAAM,EAAE,CAAC;IACT,MAAM,eAAe,GAAa,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;IACnE,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAChB,kCAAkC,QAAQ,4CAA4C,CACzF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;IACL,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vex-chat/spire",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Vex server implementation in NodeJS.",
5
5
  "type": "module",
6
6
  "files": [
@@ -23,13 +23,18 @@
23
23
  "preinstall": "npx only-allow npm",
24
24
  "start": "node --experimental-strip-types src/run.ts",
25
25
  "restart": "pm2 restart spire && pm2 logs spire",
26
- "build": "tsc",
26
+ "build": "tsc && node scripts/clean-dist-tests.mjs",
27
27
  "format": "prettier --write .",
28
28
  "format:check": "prettier --check .",
29
- "lint": "eslint src/",
30
- "lint:fix": "eslint src/ --fix",
31
- "prepare": "husky",
29
+ "lint": "eslint src/ scripts/stress/",
30
+ "lint:fix": "eslint src/ scripts/stress/ --fix",
31
+ "prepare": "husky || true",
32
32
  "test": "vitest run",
33
+ "stress:web": "node --experimental-strip-types scripts/stress/spire-stress.ts",
34
+ "stress:cli": "node --experimental-strip-types scripts/stress/stress-cli-sweep.ts",
35
+ "stress:repo-manifest": "node --experimental-strip-types scripts/stress/stress-repo-manifest.ts",
36
+ "stress:docs-pack": "node --experimental-strip-types scripts/stress/stress-docs-pack.ts",
37
+ "stress:llm-triage": "node --experimental-strip-types scripts/stress/stress-llm-triage.ts",
33
38
  "gen-spk": "node scripts/gen-spk.js",
34
39
  "docs": "node ./scripts/generate-docs.js",
35
40
  "license:check": "node scripts/fix-license-checker.mjs && npx @onebeyond/license-checker scan --allowOnly MIT MIT-0 ISC BSD-2-Clause BSD-3-Clause Apache-2.0 0BSD MPL-2.0 CC0-1.0 CC-BY-3.0 CC-BY-4.0 Unlicense BlueOak-1.0.0 Python-2.0 Zlib PSF-2.0 Artistic-2.0 AGPL-3.0-or-later WTFPL \"(MIT AND CC-BY-3.0)\" --ignoreRootPackageLicense --disableReport",
@@ -50,7 +55,9 @@
50
55
  "@types/node": "25.6.0",
51
56
  "@types/uuid": "10.0.0",
52
57
  "@types/ws": "8.18.1",
58
+ "@vex-chat/libvex": "5.1.0",
53
59
  "@vitest/eslint-plugin": "1.6.15",
60
+ "axios": "1.15.0",
54
61
  "eslint": "10.2.0",
55
62
  "eslint-config-prettier": "10.1.8",
56
63
  "eslint-plugin-n": "17.24.0",
@@ -72,6 +79,9 @@
72
79
  "*.ts": [
73
80
  "eslint --fix",
74
81
  "prettier --write"
82
+ ],
83
+ "*.{yml,yaml}": [
84
+ "prettier --write"
75
85
  ]
76
86
  },
77
87
  "overrides": {
@@ -83,8 +93,9 @@
83
93
  "dependencies": {
84
94
  "@asyncapi/web-component": "2.6.5",
85
95
  "@scalar/express-api-reference": "0.9.7",
86
- "@vex-chat/crypto": "2.0.0",
96
+ "@vex-chat/crypto": "2.0.1",
87
97
  "@vex-chat/types": "2.0.0",
98
+ "argon2": "0.44.0",
88
99
  "better-sqlite3": "11.10.0",
89
100
  "cors": "2.8.6",
90
101
  "dotenv": "17.4.1",
@@ -98,10 +109,8 @@
98
109
  "msgpackr": "1.11.8",
99
110
  "multer": "2.1.1",
100
111
  "parse-duration": "2.1.6",
101
- "picocolors": "1.1.1",
102
112
  "tweetnacl": "1.0.3",
103
113
  "uuid": "8.3.2",
104
- "winston": "3.19.0",
105
114
  "ws": "8.20.0",
106
115
  "zod": "4.3.6"
107
116
  }
@@ -11,7 +11,6 @@ import type {
11
11
  User,
12
12
  UserRecord,
13
13
  } from "@vex-chat/types";
14
- import type winston from "winston";
15
14
  import type WebSocket from "ws";
16
15
 
17
16
  import { EventEmitter } from "events";
@@ -21,11 +20,9 @@ import { xConcat, XUtils } from "@vex-chat/crypto";
21
20
  import { xSignOpen } from "@vex-chat/crypto";
22
21
  import { MailWSSchema, SocketAuthErrors } from "@vex-chat/types";
23
22
 
24
- import pc from "picocolors";
25
23
  import { parse as uuidParse, validate as uuidValidate } from "uuid";
26
24
 
27
- import { type SpireOptions, TOKEN_EXPIRY } from "./Spire.ts";
28
- import { createLogger } from "./utils/createLogger.ts";
25
+ import { TOKEN_EXPIRY } from "./Spire.ts";
29
26
  import { createUint8UUID } from "./utils/createUint8UUID.ts";
30
27
  import { msgpack } from "./utils/msgpack.ts";
31
28
 
@@ -50,7 +47,6 @@ export class ClientManager extends EventEmitter {
50
47
  private db: Database;
51
48
  private device: Device | null;
52
49
  private failed: boolean = false;
53
- private log: winston.Logger;
54
50
  private notify: (
55
51
  userID: string,
56
52
  event: string,
@@ -66,7 +62,6 @@ export class ClientManager extends EventEmitter {
66
62
  db: Database,
67
63
  notify: (userID: string, event: string, transmissionID: string) => void,
68
64
  userDetails: User,
69
- options?: SpireOptions,
70
65
  ) {
71
66
  super();
72
67
  this.conn = ws;
@@ -75,7 +70,6 @@ export class ClientManager extends EventEmitter {
75
70
  this.userDetails = userDetails;
76
71
  this.device = null;
77
72
  this.notify = notify;
78
- this.log = createLogger("client-manager", options?.logLevel || "error");
79
73
 
80
74
  this.initListeners();
81
75
  this.challenge();
@@ -96,28 +90,11 @@ export class ClientManager extends EventEmitter {
96
90
  }
97
91
 
98
92
  public send(msg: BaseMsg, header?: Uint8Array) {
99
- if (header) {
100
- this.log.debug(pc.bold(pc.red("OUTH")), header.toString());
101
- } else {
102
- this.log.debug(pc.bold(pc.red("OUTH")), emptyHeader.toString());
103
- }
104
-
105
93
  const packedMessage = packMessage(msg, header);
106
-
107
- this.log.info(
108
- pc.bold("⟶ ") +
109
- responseColor(msg.type.toUpperCase()) +
110
- " " +
111
- this.toString() +
112
- " " +
113
- pc.yellow(Buffer.byteLength(packedMessage)),
114
- );
115
-
116
- this.log.debug(pc.bold(pc.red("OUT")), msg);
117
94
  try {
118
95
  this.conn.send(packedMessage);
119
- } catch (err: unknown) {
120
- this.log.warn(String(err));
96
+ } catch (_err: unknown) {
97
+ // debugger: WS send failed
121
98
  this.fail();
122
99
  }
123
100
  }
@@ -150,7 +127,6 @@ export class ClientManager extends EventEmitter {
150
127
  if (this.failed) {
151
128
  return;
152
129
  }
153
- this.log.warn("Connection closed.");
154
130
  this.conn.close();
155
131
  this.failed = true;
156
132
  this.emit("fail");
@@ -173,12 +149,11 @@ export class ClientManager extends EventEmitter {
173
149
  this.fail();
174
150
  });
175
151
  this.conn.on("message", (message: Buffer) => {
176
- const [header, msg] = unpackMessage(message);
177
152
  const size = Buffer.byteLength(message);
178
153
 
179
154
  if (size > MAX_MSG_SIZE) {
180
155
  this.sendErr(
181
- msg.transmissionID,
156
+ "00000000-0000-0000-0000-000000000000",
182
157
  "Message is too big. Received size " +
183
158
  String(size) +
184
159
  " while max size is " +
@@ -187,16 +162,7 @@ export class ClientManager extends EventEmitter {
187
162
  return;
188
163
  }
189
164
 
190
- this.log.info(
191
- pc.bold("⟵ ") +
192
- pc.bold(msg.type.toUpperCase()) +
193
- " " +
194
- this.toString() +
195
- " " +
196
- pc.yellow(String(size)),
197
- );
198
- this.log.debug(pc.bold(pc.red("INH")), header.toString());
199
- this.log.debug(pc.bold(pc.red("IN")), msg);
165
+ const [header, msg] = unpackMessage(message);
200
166
 
201
167
  if (!msg.type) {
202
168
  this.sendErr(msg.transmissionID, "Message type is required.");
@@ -238,7 +204,6 @@ export class ClientManager extends EventEmitter {
238
204
  void this.verifyResponse(msg as RespMsg);
239
205
  break;
240
206
  default:
241
- this.log.info("unsupported message %s", msg.type);
242
207
  break;
243
208
  }
244
209
  });
@@ -266,8 +231,6 @@ export class ClientManager extends EventEmitter {
266
231
  this.getDevice().deviceID,
267
232
  this.getUser().userID,
268
233
  );
269
- this.log.info("Received mail for " + mail.recipient);
270
-
271
234
  const deviceDetails = await this.db.retrieveDevice(
272
235
  mail.recipient,
273
236
  );
@@ -288,13 +251,12 @@ export class ClientManager extends EventEmitter {
288
251
  mail.recipient,
289
252
  );
290
253
  } catch (err: unknown) {
291
- this.log.error(String(err));
292
254
  this.sendErr(msg.transmissionID, String(err));
293
255
  }
294
256
  }
295
257
  break;
296
258
  default:
297
- this.log.info("Unsupported resource type " + msg.resourceType);
259
+ break;
298
260
  }
299
261
  }
300
262
 
@@ -309,8 +271,7 @@ export class ClientManager extends EventEmitter {
309
271
  }
310
272
 
311
273
  private async pingLoop() {
312
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- intentional infinite loop
313
- while (true) {
274
+ while (!this.failed) {
314
275
  this.ping();
315
276
  await sleep(5000);
316
277
  }
@@ -384,7 +345,6 @@ export class ClientManager extends EventEmitter {
384
345
  }
385
346
  }
386
347
  if (!message) {
387
- this.log.warn("Signature verification failed!");
388
348
  this.sendAuthError(SocketAuthErrors.BadSignature);
389
349
  this.fail();
390
350
  return;
@@ -394,11 +354,9 @@ export class ClientManager extends EventEmitter {
394
354
  this.user = user;
395
355
  this.authorize(msg.transmissionID);
396
356
  } else {
397
- this.log.warn("Token is bad!");
398
357
  this.sendAuthError(SocketAuthErrors.InvalidToken);
399
358
  }
400
359
  } else {
401
- this.log.info("User is not registered.");
402
360
  this.sendAuthError(SocketAuthErrors.UserNotRegistered);
403
361
 
404
362
  this.fail();
@@ -421,14 +379,3 @@ function unpackMessage(msg: Buffer): [Uint8Array, BaseMsg] {
421
379
 
422
380
  return [msgh, msgb];
423
381
  }
424
-
425
- const responseColor = (status: string): string => {
426
- switch (status) {
427
- case "ERROR":
428
- return pc.bold(pc.red(status));
429
- case "SUCCESS":
430
- return pc.bold(pc.green(status));
431
- default:
432
- return status;
433
- }
434
- };