@vex-chat/spire 4.1.0 → 5.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"cliPasskeyPage.js","sourceRoot":"","sources":["../../src/server/cliPasskeyPage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,GAAG,GAAG;IACR,oBAAoB;IACpB,iBAAiB;IACjB,0DAA0D;IAC1D,oBAAoB;IACpB,wBAAwB;IACxB,gBAAgB;IAChB,4BAA4B;IAC5B,2BAA2B;CAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAyjBjB,CAAC;AAET,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAmB,EAAE;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACrC,GAAG,CAAC,GAAG,CAAC;YACJ,eAAe,EAAE,UAAU;YAC3B,yBAAyB,EAAE,GAAG;YAC9B,cAAc,EAAE,0BAA0B;YAC1C,oBAAoB,EAChB,uEAAuE;YAC3E,iBAAiB,EAAE,aAAa;YAChC,wBAAwB,EAAE,SAAS;SACtC,CAAC,CAAC;QACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC"}
1
+ {"version":3,"file":"cliPasskeyPage.js","sourceRoot":"","sources":["../../src/server/cliPasskeyPage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,GAAG,GAAG;IACR,oBAAoB;IACpB,iBAAiB;IACjB,0DAA0D;IAC1D,oBAAoB;IACpB,wBAAwB;IACxB,gBAAgB;IAChB,4BAA4B;IAC5B,2BAA2B;CAC9B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAg8BjB,CAAC;AAET,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAmB,EAAE;IACxD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAEhC,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QACrC,GAAG,CAAC,GAAG,CAAC;YACJ,eAAe,EAAE,UAAU;YAC3B,yBAAyB,EAAE,GAAG;YAC9B,cAAc,EAAE,0BAA0B;YAC1C,oBAAoB,EAChB,uEAAuE;YAC3E,iBAAiB,EAAE,aAAa;YAChC,wBAAwB,EAAE,SAAS;SACtC,CAAC,CAAC;QACH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC"}
@@ -4,8 +4,7 @@
4
4
  * Commercial licenses available at vex.wtf
5
5
  */
6
6
  /**
7
- * Default HTTP/WS port for the Spire API (tweetnacl and FIPS), unless
8
- * `API_PORT` / `apiPort` is set. Clients can tell profiles apart via `GET /status`.
7
+ * Default HTTP/WS port for the Spire API unless `API_PORT` / `apiPort` is set.
9
8
  */
10
9
  export declare const DEFAULT_SPIRE_API_PORT = 16777;
11
10
  /**
@@ -4,8 +4,7 @@
4
4
  * Commercial licenses available at vex.wtf
5
5
  */
6
6
  /**
7
- * Default HTTP/WS port for the Spire API (tweetnacl and FIPS), unless
8
- * `API_PORT` / `apiPort` is set. Clients can tell profiles apart via `GET /status`.
7
+ * Default HTTP/WS port for the Spire API unless `API_PORT` / `apiPort` is set.
9
8
  */
10
9
  export const DEFAULT_SPIRE_API_PORT = 16777;
11
10
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"spireListenPort.js","sourceRoot":"","sources":["../src/spireListenPort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE5C;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA4B;IAC/D,IACI,OAAO,QAAQ,KAAK,QAAQ;QAC5B,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACzB,QAAQ,GAAG,CAAC,EACd,CAAC;QACC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,sBAAsB,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"spireListenPort.js","sourceRoot":"","sources":["../src/spireListenPort.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE5C;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA4B;IAC/D,IACI,OAAO,QAAQ,KAAK,QAAQ;QAC5B,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACzB,QAAQ,GAAG,CAAC,EACd,CAAC;QACC,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,sBAAsB,CAAC;AAClC,CAAC"}
@@ -3,7 +3,6 @@
3
3
  * Licensed under AGPL-3.0. See LICENSE for details.
4
4
  * Commercial licenses available at vex.wtf
5
5
  */
6
- export declare function isSpireFipsEnabled(value: string | undefined): boolean;
7
6
  export declare function loadEnv(): void;
8
7
  export declare function normalizeEnvValue(value: string): string;
9
8
  export declare function validateSpireRuntimeEnv(env: Record<string, string | undefined>): void;
@@ -9,7 +9,6 @@ const NORMALIZED_ENV_VARS = [
9
9
  ...REQUIRED_ENV_VARS,
10
10
  "API_PORT",
11
11
  "SPIRE_TRUST_PROXY_HOPS",
12
- "SPIRE_FIPS",
13
12
  "SPIRE_PASSKEY_RP_ID",
14
13
  "SPIRE_PASSKEY_RP_NAME",
15
14
  "SPIRE_PASSKEY_ORIGINS",
@@ -19,10 +18,6 @@ const NORMALIZED_ENV_VARS = [
19
18
  ];
20
19
  const HEX_BYTES_RE = /^(?:[0-9a-fA-F]{2})+$/;
21
20
  const TWEETNACL_SPK_HEX_LENGTH = 128;
22
- export function isSpireFipsEnabled(value) {
23
- const normalized = value == null ? "" : normalizeEnvValue(value);
24
- return normalized === "1" || normalized === "true";
25
- }
26
21
  /* Populate process.env with vars from .env and verify required vars are present. */
27
22
  export function loadEnv() {
28
23
  config();
@@ -51,18 +46,13 @@ export function validateSpireRuntimeEnv(env) {
51
46
  const spk = normalizeEnvValue(env["SPK"] ?? "");
52
47
  const jwtSecret = normalizeEnvValue(env["JWT_SECRET"] ?? "");
53
48
  if (!HEX_BYTES_RE.test(spk)) {
54
- throw new Error("SPK must be an even-length hex string. Generate one with `pnpm --filter @vex-chat/spire gen-spk` or `gen-spk-fips`.");
55
- }
56
- if (isSpireFipsEnabled(env["SPIRE_FIPS"])) {
57
- if (spk.length === TWEETNACL_SPK_HEX_LENGTH) {
58
- throw new Error("SPIRE_FIPS=true requires an SPK from `pnpm --filter @vex-chat/spire gen-spk-fips`; the configured SPK looks like a tweetnacl key.");
59
- }
49
+ throw new Error("SPK must be an even-length hex string. Generate one with `pnpm --filter @vex-chat/spire gen-spk`.");
60
50
  }
61
- else if (spk.length !== TWEETNACL_SPK_HEX_LENGTH) {
62
- throw new Error("SPIRE_FIPS is not enabled, so SPK must be 128 hex characters from `pnpm --filter @vex-chat/spire gen-spk`.");
51
+ if (spk.length !== TWEETNACL_SPK_HEX_LENGTH) {
52
+ throw new Error("SPK must be 128 hex characters from `pnpm --filter @vex-chat/spire gen-spk`.");
63
53
  }
64
54
  if (jwtSecret.length === 0) {
65
- throw new Error("JWT_SECRET must be set. Generate one with `pnpm --filter @vex-chat/spire gen-spk` or `gen-spk-fips`.");
55
+ throw new Error("JWT_SECRET must be set. Generate one with `pnpm --filter @vex-chat/spire gen-spk`.");
66
56
  }
67
57
  if (jwtSecret.length < 32) {
68
58
  throw new Error("JWT_SECRET must contain at least 32 characters.");
@@ -1 +1 @@
1
- {"version":3,"file":"loadEnv.js","sourceRoot":"","sources":["../../src/utils/loadEnv.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loadEnv.js","sourceRoot":"","sources":["../../src/utils/loadEnv.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,5 @@
3
3
  * Licensed under AGPL-3.0. See LICENSE for details.
4
4
  * Commercial licenses available at vex.wtf
5
5
  */
6
- /**
7
- * Ed25519 detached-verify open: sync on tweetnacl, async for FIPS (Web Crypto).
8
- */
6
+ /** Ed25519 detached-verify open. */
9
7
  export declare function spireXSignOpenAsync(signedMessage: Uint8Array, publicKey: Uint8Array): Promise<null | Uint8Array>;
@@ -3,14 +3,9 @@
3
3
  * Licensed under AGPL-3.0. See LICENSE for details.
4
4
  * Commercial licenses available at vex.wtf
5
5
  */
6
- import { getCryptoProfile, xSignOpen, xSignOpenAsync } from "@vex-chat/crypto";
7
- /**
8
- * Ed25519 detached-verify open: sync on tweetnacl, async for FIPS (Web Crypto).
9
- */
10
- export async function spireXSignOpenAsync(signedMessage, publicKey) {
11
- if (getCryptoProfile() === "fips") {
12
- return xSignOpenAsync(signedMessage, publicKey);
13
- }
14
- return xSignOpen(signedMessage, publicKey);
6
+ import { xSignOpen } from "@vex-chat/crypto";
7
+ /** Ed25519 detached-verify open. */
8
+ export function spireXSignOpenAsync(signedMessage, publicKey) {
9
+ return Promise.resolve(xSignOpen(signedMessage, publicKey));
15
10
  }
16
11
  //# sourceMappingURL=spireXSignOpenAsync.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"spireXSignOpenAsync.js","sourceRoot":"","sources":["../../src/utils/spireXSignOpenAsync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE/E;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACrC,aAAyB,EACzB,SAAqB;IAErB,IAAI,gBAAgB,EAAE,KAAK,MAAM,EAAE,CAAC;QAChC,OAAO,cAAc,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"spireXSignOpenAsync.js","sourceRoot":"","sources":["../../src/utils/spireXSignOpenAsync.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,oCAAoC;AACpC,MAAM,UAAU,mBAAmB,CAC/B,aAAyB,EACzB,SAAqB;IAErB,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC;AAChE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vex-chat/spire",
3
- "version": "4.1.0",
3
+ "version": "5.0.0",
4
4
  "description": "Vex server implementation in NodeJS.",
5
5
  "type": "module",
6
6
  "files": [
@@ -55,7 +55,7 @@
55
55
  "typescript-eslint": "8.58.1",
56
56
  "vitest": "4.1.4",
57
57
  "@vex-chat/eslint-config": "0.0.1",
58
- "@vex-chat/libvex": "^9.1.0"
58
+ "@vex-chat/libvex": "^10.0.0"
59
59
  },
60
60
  "typeCoverage": {
61
61
  "atLeast": 95,
@@ -83,7 +83,7 @@
83
83
  "uuid": "^14.0.0",
84
84
  "ws": "8.21.0",
85
85
  "zod": "^4.3.6",
86
- "@vex-chat/crypto": "^11.0.0",
86
+ "@vex-chat/crypto": "^12.0.0",
87
87
  "@vex-chat/types": "^4.3.3"
88
88
  },
89
89
  "scripts": {
@@ -108,7 +108,6 @@
108
108
  "stress:docs-pack": "pnpm run integration:docs-pack",
109
109
  "stress:llm-triage": "pnpm run integration:llm-triage",
110
110
  "gen-spk": "node scripts/gen-spk.js",
111
- "gen-spk-fips": "node scripts/gen-spk-fips.js",
112
111
  "docs": "node ./scripts/generate-docs.js",
113
112
  "service:status-monitor": "node ./services/status-monitor/index.js",
114
113
  "service:status-monitor:clear-history": "node ./services/status-monitor/clear-history.js",
package/src/Database.ts CHANGED
@@ -41,11 +41,7 @@ import * as fs from "node:fs/promises";
41
41
  import path from "node:path";
42
42
  import { fileURLToPath, pathToFileURL } from "node:url";
43
43
 
44
- import {
45
- fipsEcdhRawPublicKeyFromEcdsaSpkiAsync,
46
- getCryptoProfile,
47
- XUtils,
48
- } from "@vex-chat/crypto";
44
+ import { XUtils } from "@vex-chat/crypto";
49
45
  import {
50
46
  ACCOUNT_PASSWORD_MAX_LENGTH,
51
47
  ACCOUNT_PASSWORD_MIN_LENGTH,
@@ -677,15 +673,10 @@ export class Database extends EventEmitter {
677
673
  if (!preKey) {
678
674
  throw new Error("Failed to get prekey.");
679
675
  }
680
- const signKeyBytes = XUtils.decodeHex(device.signKey);
681
- const signKey =
682
- getCryptoProfile() === "fips"
683
- ? await fipsEcdhRawPublicKeyFromEcdsaSpkiAsync(signKeyBytes)
684
- : signKeyBytes;
685
676
  const keyBundle: KeyBundle = {
686
677
  otk,
687
678
  preKey,
688
- signKey,
679
+ signKey: XUtils.decodeHex(device.signKey),
689
680
  };
690
681
  return keyBundle;
691
682
  }
package/src/Spire.ts CHANGED
@@ -25,10 +25,8 @@ import express from "express";
25
25
  import { XUtils } from "@vex-chat/crypto";
26
26
  import {
27
27
  type KeyPair,
28
- setCryptoProfile,
29
28
  xRandomBytes,
30
29
  xSignKeyPairFromSecret,
31
- xSignKeyPairFromSecretAsync,
32
30
  } from "@vex-chat/crypto";
33
31
  import {
34
32
  ACCOUNT_PASSWORD_MAX_LENGTH,
@@ -238,52 +236,18 @@ const getCommitSha = (): string => {
238
236
  export interface SpireOptions {
239
237
  /**
240
238
  * TCP port for the HTTP/WS server. If omitted, `run.ts` + `resolveSpireListenPort`
241
- * use the default (16777 for all profiles; crypto mode is in `GET /status`). Env: `API_PORT`.
239
+ * use the default (16777). Env: `API_PORT`.
242
240
  */
243
241
  apiPort?: number;
244
- /** Default `tweetnacl`. For `fips`, use `Spire.createAsync` (FIPS key load is async). */
245
- cryptoProfile?: "fips" | "tweetnacl";
246
242
  dbType?: "mysql" | "sqlite3" | "sqlite3mem" | "sqlite";
247
243
  }
248
244
 
249
- export function resolveTrustProxyHops(value: string | undefined): number {
250
- if (value === undefined || value.trim() === "") {
251
- return 0;
252
- }
253
- const hops = Number(value);
254
- if (!Number.isInteger(hops) || hops < 0 || hops > 10) {
255
- throw new Error(
256
- "SPIRE_TRUST_PROXY_HOPS must be an integer from 0 to 10.",
257
- );
258
- }
259
- return hops;
260
- }
261
-
262
- /**
263
- * Masks identifying material in the access-log URL: hyphenated UUIDs, and
264
- * `/device/...` path segments that hold public-key material (32B ed25519 hex, or
265
- * a longer P-256 SPKI hex in FIPS) — not the same pattern as a UUID.
266
- */
267
- function redactAccessLogUrl(url: string): string {
268
- let s = url.replace(
269
- /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
270
- "[uuid]",
271
- );
272
- // Device id is a hex public key, not a UUID: strip the segment after /device/
273
- s = s.replace(/(\/device\/)([0-9a-fA-F]{16,})(?=[/?#]|$)/g, "$1[device]");
274
- return s;
275
- }
276
-
277
- /** FIPS: sign key loaded inside `Spire.createAsync` before the constructor runs. */
278
- let pendingFipsKeyPair: KeyPair | null = null;
279
-
280
245
  export class Spire extends EventEmitter {
281
246
  private actionTokens = new Map<string, ActionToken>();
282
247
  private api = express();
283
248
  private calls: CallManager;
284
249
  private clients: ClientManager[] = [];
285
250
  private readonly commitSha = getCommitSha();
286
- private readonly cryptoProfile: "fips" | "tweetnacl";
287
251
  private db: Database;
288
252
  private dbReady = false;
289
253
  private deviceChallenges = new Map<
@@ -309,19 +273,7 @@ export class Spire extends EventEmitter {
309
273
 
310
274
  constructor(SK: string, options?: SpireOptions) {
311
275
  super();
312
- this.cryptoProfile = options?.cryptoProfile ?? "tweetnacl";
313
- if (pendingFipsKeyPair) {
314
- this.signKeys = pendingFipsKeyPair;
315
- pendingFipsKeyPair = null;
316
- } else {
317
- if (this.cryptoProfile === "fips") {
318
- throw new Error(
319
- 'FIPS: use `await Spire.createAsync(secretKeyHex, { ...options, cryptoProfile: "fips" })` instead of `new Spire()`.',
320
- );
321
- }
322
- setCryptoProfile(this.cryptoProfile);
323
- this.signKeys = xSignKeyPairFromSecret(XUtils.decodeHex(SK));
324
- }
276
+ this.signKeys = xSignKeyPairFromSecret(XUtils.decodeHex(SK));
325
277
 
326
278
  // Proxy trust must match the deployment topology. Defaulting to zero
327
279
  // prevents direct deployments from accepting attacker-supplied
@@ -363,33 +315,6 @@ export class Spire extends EventEmitter {
363
315
  this.init(resolveSpireListenPort(options?.apiPort));
364
316
  }
365
317
 
366
- /**
367
- * Construct Spire when the crypto profile is `fips` (async sign-key derivation).
368
- * For `tweetnacl` (default) you can use `new Spire()` directly.
369
- */
370
- public static async createAsync(
371
- secretKeyHex: string,
372
- options?: SpireOptions,
373
- ): Promise<Spire> {
374
- if ((options?.cryptoProfile ?? "tweetnacl") !== "fips") {
375
- return new Spire(secretKeyHex, options);
376
- }
377
- if (typeof globalThis.crypto.subtle !== "object") {
378
- throw new Error(
379
- "FIPS: Spire requires `globalThis.crypto.subtle` (e.g. Node 20+ with global Web Crypto).",
380
- );
381
- }
382
- setCryptoProfile("fips");
383
- try {
384
- pendingFipsKeyPair = await xSignKeyPairFromSecretAsync(
385
- XUtils.decodeHex(secretKeyHex),
386
- );
387
- return new Spire(secretKeyHex, options);
388
- } finally {
389
- pendingFipsKeyPair = null;
390
- }
391
- }
392
-
393
318
  public async close(): Promise<void> {
394
319
  if (this.mailPruneInterval) {
395
320
  clearInterval(this.mailPruneInterval);
@@ -671,7 +596,7 @@ export class Spire extends EventEmitter {
671
596
 
672
597
  const ok = dbHealthy;
673
598
  if (!devApiKeySkipsRateLimits(req)) {
674
- res.json({ cryptoProfile: this.cryptoProfile, ok });
599
+ res.json({ ok });
675
600
  return;
676
601
  }
677
602
  const canaryEnv = process.env["CANARY"]?.trim().toLowerCase();
@@ -681,7 +606,6 @@ export class Spire extends EventEmitter {
681
606
  canaryEnv === "true" ||
682
607
  canaryEnv === "yes",
683
608
  checkDurationMs,
684
- cryptoProfile: this.cryptoProfile,
685
609
  now: new Date(),
686
610
  ok,
687
611
  version: this.version,
@@ -1438,7 +1362,7 @@ export class Spire extends EventEmitter {
1438
1362
  const message =
1439
1363
  err instanceof Error ? err.message : String(err);
1440
1364
  console.error(
1441
- `[spire] /register failed requestId=${requestId} profile=${this.cryptoProfile} message=${message}`,
1365
+ `[spire] /register failed requestId=${requestId} message=${message}`,
1442
1366
  );
1443
1367
  if (err instanceof Error && err.stack) {
1444
1368
  console.error(err.stack);
@@ -1510,6 +1434,19 @@ export class Spire extends EventEmitter {
1510
1434
  }
1511
1435
  }
1512
1436
 
1437
+ export function resolveTrustProxyHops(value: string | undefined): number {
1438
+ if (value === undefined || value.trim() === "") {
1439
+ return 0;
1440
+ }
1441
+ const hops = Number(value);
1442
+ if (!Number.isInteger(hops) || hops < 0 || hops > 10) {
1443
+ throw new Error(
1444
+ "SPIRE_TRUST_PROXY_HOPS must be an integer from 0 to 10.",
1445
+ );
1446
+ }
1447
+ return hops;
1448
+ }
1449
+
1513
1450
  // Usernames are case-insensitive at the protocol level — `User` and
1514
1451
  // `user` must resolve to the same account. We canonicalize to
1515
1452
  // lowercase at the registration boundary so the persisted row, the
@@ -1518,3 +1455,17 @@ export class Spire extends EventEmitter {
1518
1455
  function normalizeRegistrationUsername(username: string): string {
1519
1456
  return username.trim().toLowerCase();
1520
1457
  }
1458
+
1459
+ /**
1460
+ * Masks identifying material in the access-log URL: hyphenated UUIDs, and
1461
+ * `/device/...` path segments that hold Ed25519 public-key material.
1462
+ */
1463
+ function redactAccessLogUrl(url: string): string {
1464
+ let s = url.replace(
1465
+ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,
1466
+ "[uuid]",
1467
+ );
1468
+ // Device id is a hex public key, not a UUID: strip the segment after /device/
1469
+ s = s.replace(/(\/device\/)([0-9a-fA-F]{16,})(?=[/?#]|$)/g, "$1[device]");
1470
+ return s;
1471
+ }
@@ -44,15 +44,25 @@ describe("CLI passkey page", () => {
44
44
  expect(res.headers.get("permissions-policy")).toContain(
45
45
  "publickey-credentials-create=(self)",
46
46
  );
47
- expect(html).toContain("Continue with your passkey.");
47
+ expect(html).toContain("Continue with your passkey");
48
+ expect(html).toContain("--accent: #e5484d");
49
+ expect(html).not.toContain("radial-gradient");
50
+ expect(html).toContain('role="status" aria-live="polite"');
48
51
  expect(html).toContain("resolveTrustedApiBase");
49
52
  expect(html).toContain("Passkey link API origin is not trusted.");
53
+ expect(html).toContain("resolveCompletionCallback");
54
+ expect(html).toContain('callback.protocol !== "vex:" ||');
55
+ expect(html).toContain('callback.pathname !== "/complete" ||');
50
56
  expect(html).toContain("window.history.replaceState");
51
57
  expect(html).toContain('mode === "register-handoff"');
52
58
  expect(html).toContain('mode === "authenticate-handoff"');
53
59
  expect(html).toContain('"/auth/passkey/browser-registration/"');
54
60
  expect(html).toContain('"/auth/passkey/browser-authentication/"');
55
61
  expect(html).toContain("action.disabled = nextBusy || completed");
62
+ expect(html).toContain("showCompletion(");
63
+ expect(html).toContain("action.hidden = true");
64
+ expect(html).toContain('err.name === "NotAllowedError"');
65
+ expect(html).toContain("apiRequestError(response.status, payload)");
56
66
  expect(html).toContain("navigator.credentials.create");
57
67
  expect(html).toContain("navigator.credentials.get");
58
68
  });
@@ -7,13 +7,11 @@
7
7
  import { describe, expect, it } from "vitest";
8
8
 
9
9
  import {
10
- isSpireFipsEnabled,
11
10
  normalizeEnvValue,
12
11
  validateSpireRuntimeEnv,
13
12
  } from "../utils/loadEnv.ts";
14
13
 
15
14
  const TWEETNACL_SPK = "ab".repeat(64);
16
- const FIPS_SPK = "cd".repeat(90);
17
15
  const JWT_SECRET = "ef".repeat(32);
18
16
 
19
17
  describe("normalizeEnvValue", () => {
@@ -30,22 +28,11 @@ describe("normalizeEnvValue", () => {
30
28
  });
31
29
  });
32
30
 
33
- describe("isSpireFipsEnabled", () => {
34
- it("accepts true and 1 only", () => {
35
- expect(isSpireFipsEnabled("true")).toBe(true);
36
- expect(isSpireFipsEnabled('"true"')).toBe(true);
37
- expect(isSpireFipsEnabled("1")).toBe(true);
38
- expect(isSpireFipsEnabled("false")).toBe(false);
39
- expect(isSpireFipsEnabled(undefined)).toBe(false);
40
- });
41
- });
42
-
43
31
  describe("validateSpireRuntimeEnv", () => {
44
32
  it("accepts quoted compose-safe tweetnacl keys", () => {
45
33
  expect(() => {
46
34
  validateSpireRuntimeEnv({
47
35
  JWT_SECRET: `"${JWT_SECRET}"`,
48
- SPIRE_FIPS: "false",
49
36
  SPK: `"${TWEETNACL_SPK}"`,
50
37
  });
51
38
  }).not.toThrow();
@@ -60,22 +47,11 @@ describe("validateSpireRuntimeEnv", () => {
60
47
  }).toThrow(/SPK must be an even-length hex string/);
61
48
  });
62
49
 
63
- it("rejects a FIPS flag with a tweetnacl-sized SPK", () => {
64
- expect(() => {
65
- validateSpireRuntimeEnv({
66
- JWT_SECRET,
67
- SPIRE_FIPS: "true",
68
- SPK: TWEETNACL_SPK,
69
- });
70
- }).toThrow(/requires an SPK from .*gen-spk-fips/);
71
- });
72
-
73
- it("rejects a FIPS-sized SPK without the FIPS flag", () => {
50
+ it("rejects an SPK with the wrong length", () => {
74
51
  expect(() => {
75
52
  validateSpireRuntimeEnv({
76
53
  JWT_SECRET,
77
- SPIRE_FIPS: "false",
78
- SPK: FIPS_SPK,
54
+ SPK: "cd".repeat(90),
79
55
  });
80
56
  }).toThrow(/SPK must be 128 hex characters/);
81
57
  });
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import {
8
- setCryptoProfile,
9
8
  xBoxKeyPairAsync,
10
9
  xPreKeySignaturePayload,
11
10
  xSignAsync,
@@ -13,7 +12,7 @@ import {
13
12
  XUtils,
14
13
  } from "@vex-chat/crypto";
15
14
 
16
- import { beforeEach, describe, expect, it } from "vitest";
15
+ import { describe, expect, it } from "vitest";
17
16
 
18
17
  import {
19
18
  verifyDevicePayloadPreKeySignature,
@@ -31,10 +30,6 @@ async function makeSignedPreKey() {
31
30
  }
32
31
 
33
32
  describe("prekey signature validation", () => {
34
- beforeEach(() => {
35
- setCryptoProfile("tweetnacl");
36
- });
37
-
38
33
  it("accepts a prekey signed by the device signing key", async () => {
39
34
  const { preKey, signature, signKeys } = await makeSignedPreKey();
40
35
 
@@ -12,7 +12,7 @@ import {
12
12
  } from "../spireListenPort.ts";
13
13
 
14
14
  describe("resolveSpireListenPort", () => {
15
- it("uses default for tweetnacl and fips when no explicit port", () => {
15
+ it("uses the default when no explicit port is provided", () => {
16
16
  expect(resolveSpireListenPort(undefined)).toBe(DEFAULT_SPIRE_API_PORT);
17
17
  });
18
18
 
package/src/run.ts CHANGED
@@ -9,7 +9,7 @@ import type { SpireOptions } from "./Spire.ts";
9
9
  import { Spire } from "./Spire.ts";
10
10
  import { loadEnv } from "./utils/loadEnv.ts";
11
11
 
12
- async function main() {
12
+ function main() {
13
13
  // load the environment variables — loadEnv() exits if required vars are missing
14
14
  loadEnv();
15
15
 
@@ -29,21 +29,12 @@ async function main() {
29
29
  }
30
30
  }
31
31
  const dbType = parseDbType(process.env["DB_TYPE"]);
32
- const fips =
33
- process.env["SPIRE_FIPS"] === "1" ||
34
- process.env["SPIRE_FIPS"] === "true";
35
-
36
32
  const options: SpireOptions = {
37
33
  ...(apiPort !== undefined ? { apiPort } : {}),
38
34
  ...(dbType !== undefined ? { dbType } : {}),
39
- ...(fips ? { cryptoProfile: "fips" } : {}),
40
35
  };
41
36
 
42
- if (fips) {
43
- await Spire.createAsync(spk, options);
44
- } else {
45
- new Spire(spk, options);
46
- }
37
+ new Spire(spk, options);
47
38
  }
48
39
 
49
40
  function parseDbType(value: string | undefined): SpireOptions["dbType"] {
@@ -58,7 +49,9 @@ function parseDbType(value: string | undefined): SpireOptions["dbType"] {
58
49
  }
59
50
  }
60
51
 
61
- void main().catch((err: unknown) => {
52
+ try {
53
+ main();
54
+ } catch (err: unknown) {
62
55
  console.error(err);
63
56
  process.exit(1);
64
- });
57
+ }