@wrongstack/cli 0.306.3 → 0.307.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.
Files changed (76) hide show
  1. package/dist/{auth-L3WGSGDX.js → auth-TVOOHL4Q.js} +45 -106
  2. package/dist/boot/short-circuit-flags.d.ts +2 -2
  3. package/dist/chimera-cascade-evidence.d.ts +1 -1
  4. package/dist/chunk-2VMJ3E5Z.js +71 -0
  5. package/dist/{chunk-5N34EWJR.js → chunk-2ZNBRZ4W.js} +2 -2
  6. package/dist/{chunk-RHLQT3EV.js → chunk-CB35UVXO.js} +1636 -1528
  7. package/dist/{chunk-YKBRFSGL.js → chunk-DOJLAULS.js} +28 -28
  8. package/dist/{chunk-JUHRI6OV.js → chunk-G7337TTK.js} +24 -5
  9. package/dist/{chunk-KXN5HZDL.js → chunk-PKHGLWUJ.js} +24 -6
  10. package/dist/{chunk-HITHEXE3.js → chunk-TOG7OMKJ.js} +2 -2
  11. package/dist/{chunk-ARWGQUB3.js → chunk-XSG33WHN.js} +15 -5
  12. package/dist/{cli-main-3PUWFKSL.js → cli-main-YDOZZOZA.js} +29026 -28205
  13. package/dist/cli-main.d.ts +0 -20
  14. package/dist/{execution-4IG5XKOL.js → execution-CGT6J5ZN.js} +306 -274
  15. package/dist/fleet/host-learning-scheduler.d.ts +27 -0
  16. package/dist/fleet/host-shadow-manager.d.ts +64 -0
  17. package/dist/fleet/host.d.ts +5 -140
  18. package/dist/{hq-ZK6PGN3W.js → hq-PA6RH2UW.js} +2 -2
  19. package/dist/hq-server/mailbox-gateway-manager.d.ts +32 -0
  20. package/dist/hq-server/routes/auth/bootstrap-routes.d.ts +5 -0
  21. package/dist/hq-server/routes/auth/common.d.ts +11 -0
  22. package/dist/hq-server/routes/auth/password-routes.d.ts +9 -0
  23. package/dist/hq-server/routes/auth/session-audit-routes.d.ts +6 -0
  24. package/dist/hq-server/routes/auth/totp-routes.d.ts +9 -0
  25. package/dist/hq-server/routes/auth-handlers.d.ts +5 -132
  26. package/dist/hq-server/routes/command-handlers.d.ts +7 -0
  27. package/dist/hq-server/routes/data-handlers.d.ts +8 -0
  28. package/dist/hq-server/routes/mailbox-handlers.d.ts +22 -0
  29. package/dist/hq-server/routes.d.ts +3 -3
  30. package/dist/hq-server/server-lifecycle.d.ts +37 -0
  31. package/dist/hq-server/snapshot.d.ts +6 -5
  32. package/dist/hq-server/types.d.ts +4 -0
  33. package/dist/hq-server/upgrade-handler.d.ts +34 -0
  34. package/dist/hq-server/ws-client-events.d.ts +25 -0
  35. package/dist/hq-server/ws-leader.d.ts +47 -0
  36. package/dist/hq-server/ws-resume.d.ts +27 -0
  37. package/dist/hq-server/ws.d.ts +4 -71
  38. package/dist/{hq-server-IVZLVCFV.js → hq-server-PBRU275L.js} +2 -2
  39. package/dist/hq-server.d.ts +0 -38
  40. package/dist/index.js +1267 -122
  41. package/dist/{mcp-JNWEQ6T2.js → mcp-CMLFHRG2.js} +19 -9
  42. package/dist/mcp-serve.d.ts +31 -1
  43. package/dist/{modeldiag-6VS5KDP5.js → modeldiag-ICMPHT5L.js} +868 -831
  44. package/dist/{project-manifest-GE25PW4S.js → project-manifest-YFTJ3D5N.js} +2 -2
  45. package/dist/{project-picker-BOCWHBXP.js → project-picker-R5TYAE3Q.js} +3 -3
  46. package/dist/{providers-models-HQN3AT3K.js → providers-models-4FAKM2WO.js} +4 -4
  47. package/dist/repl-eternal-loop.d.ts +8 -0
  48. package/dist/repl-goal-banner.d.ts +21 -0
  49. package/dist/slash-commands/brain-council.d.ts +36 -0
  50. package/dist/slash-commands/brain-status.d.ts +12 -0
  51. package/dist/slash-commands/fallback.d.ts +6 -0
  52. package/dist/slash-commands/kanban-board-handlers.d.ts +13 -0
  53. package/dist/slash-commands/kanban-lifecycle-diagnostics.d.ts +16 -0
  54. package/dist/slash-commands/kanban-task-subcommands.d.ts +8 -0
  55. package/dist/slash-commands/memory-audience.d.ts +14 -0
  56. package/dist/slash-commands/memory-gather.d.ts +9 -0
  57. package/dist/slash-commands/memory-stats.d.ts +5 -0
  58. package/dist/slash-commands/settings-mutations.d.ts +6 -0
  59. package/dist/slash-commands/settings-view.d.ts +5 -0
  60. package/dist/subcommands/handlers/modeldiag-bench.d.ts +10 -0
  61. package/dist/subcommands/handlers/modeldiag-eval.d.ts +27 -0
  62. package/dist/subcommands/handlers/modeldiag-test.d.ts +8 -0
  63. package/dist/{version-help-ASKQYXDJ.js → version-help-TFD6VPF3.js} +2 -2
  64. package/dist/vibe-protocol-wiring.d.ts +10 -0
  65. package/dist/webui-server/credential-watcher.d.ts +15 -0
  66. package/dist/webui-server/domain-handlers.d.ts +21 -0
  67. package/dist/webui-server/route-contexts.d.ts +41 -0
  68. package/dist/{webui-server-FTG7JAZU.js → webui-server-IPHGJGXE.js} +515 -437
  69. package/dist/wiring/cli-execute-builder.d.ts +88 -0
  70. package/dist/wiring/cli-heap-watchdog.d.ts +33 -0
  71. package/dist/wiring/cli-prompt-and-tools-setup.d.ts +25 -0
  72. package/dist/wiring/cli-slash-commands-setup.d.ts +79 -0
  73. package/dist/wiring/heap-watchdog-setup.d.ts +53 -0
  74. package/dist/wiring/metrics.d.ts +1 -0
  75. package/dist/wiring/replay-governance-setup.d.ts +31 -0
  76. package/package.json +27 -27
@@ -44,11 +44,12 @@ export declare function sendGuarded(ws: WebSocket, data: string): boolean;
44
44
  * That is both retained memory and, worse, permanent weight in every
45
45
  * `buildSnapshot()` broadcast to every browser.
46
46
  *
47
- * A live session republishes unconditionally every 2.5s (`session-bridge.ts`
48
- * forces the snapshot on a timer, bypassing its own dedupe hash), so
49
- * `receivedAt` is a true liveness signal rather than a change signal. Five
50
- * minutes is 120 consecutive missed publishes far past any GC pause,
51
- * scheduler stall, or transient hiccup, so this never evicts a live session.
47
+ * A live session refreshes `receivedAt` with every changed snapshot, and
48
+ * `session-bridge.ts` additionally forces a keep-alive republish every
49
+ * 4 minutes when nothing changed (well inside this window), so `receivedAt`
50
+ * is a true liveness signal rather than a change signal. Five minutes is
51
+ * far past any GC pause, scheduler stall, or transient hiccup and beyond
52
+ * the keep-alive cadence — so this never evicts a live session.
52
53
  */
53
54
  export declare const HQ_STALE_SNAPSHOT_MS: number;
54
55
  /**
@@ -148,4 +148,8 @@ export interface ProjectDetail {
148
148
  clients: readonly HqClientRecord[];
149
149
  mailboxes: readonly HqMailboxSnapshotPayload[];
150
150
  }
151
+ export interface HqRouterMailboxGateway {
152
+ mailbox: import('@wrongstack/core/coordination').Mailbox;
153
+ router: ReturnType<typeof import('@wrongstack/core/coordination').createMailboxHttpRouter>;
154
+ }
151
155
  //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * WebSocket upgrade and connection dispatch for HQ server.
3
+ *
4
+ * @module hq-server/upgrade-handler
5
+ */
6
+ import type { IncomingMessage } from 'node:http';
7
+ import type { Duplex } from 'node:stream';
8
+ import { type HqToken } from '@wrongstack/core/hq';
9
+ import type { WebSocket, WebSocketServer } from 'ws';
10
+ import type { HqAuthState } from './auth-state.js';
11
+ import type { ConnectedClient, HqSessionEntry, TranscriptRing } from './types.js';
12
+ export interface HqUpgradeHandlerDeps {
13
+ host: string;
14
+ port: number;
15
+ listeningPort: () => number;
16
+ trustedPublicOrigins: Set<string>;
17
+ allowFileOrigin?: boolean | undefined;
18
+ requireBrowserAuth?: boolean | undefined;
19
+ mutableAuth: HqAuthState['mutableAuth'];
20
+ sessions: Map<string, HqSessionEntry>;
21
+ clients: Map<WebSocket, ConnectedClient>;
22
+ clientSocketTokens: Map<WebSocket, HqToken | undefined>;
23
+ browsers: Set<WebSocket>;
24
+ eventLog: import('@wrongstack/core/hq').HqEventEnvelope[];
25
+ transcripts: Map<string, TranscriptRing>;
26
+ agentMessages: Map<string, import('@wrongstack/core/hq').HqTranscriptEntry[]>;
27
+ persistence: import('@wrongstack/core/hq').HqPersistence;
28
+ auditLog: import('@wrongstack/core/hq').HqCommandAuditLog;
29
+ snapshotBroadcaster: ReturnType<typeof import('./snapshot.js').createSnapshotBroadcaster>;
30
+ wss: WebSocketServer;
31
+ }
32
+ export declare function handleHqUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer, deps: HqUpgradeHandlerDeps): void;
33
+ export declare function handleHqConnection(ws: WebSocket, req: IncomingMessage, pathname: string, deps: HqUpgradeHandlerDeps): void;
34
+ //# sourceMappingURL=upgrade-handler.d.ts.map
@@ -0,0 +1,25 @@
1
+ /**
2
+ * HQ server — Inbound client event handling and Kanban fanout.
3
+ *
4
+ * @module hq-server/ws-client-events
5
+ */
6
+ import type { HqEventEnvelope, HqPersistence, HqRedactionPolicy, HqToken, HqTranscriptEntry } from '@wrongstack/core/hq';
7
+ import type { WebSocket } from 'ws';
8
+ import type { ConnectedClient, HqSnapshotBroadcaster, TranscriptRing } from './types.js';
9
+ /**
10
+ * Server-side fanout of a `hq.kanban_snapshot` envelope after a merge.
11
+ *
12
+ * C2 — broadcasts the post-merge `delta` payload to every client whose
13
+ * `projectId` matches `projectId`.
14
+ *
15
+ * Exported for direct unit testing in `tests/hq-kanban-fanout.test.ts`.
16
+ */
17
+ export declare function fanoutKanbanDelta(message: string, clients: Map<WebSocket, ConnectedClient>, _browsers: Set<WebSocket>, projectId: string): {
18
+ clientsNotified: number;
19
+ browsersNotified: number;
20
+ };
21
+ export declare function handleIncomingClientEvent(ws: WebSocket, incomingEvent: HqEventEnvelope, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, auth: {
22
+ token?: HqToken | undefined;
23
+ getOperatorPolicy: () => Partial<HqRedactionPolicy> | undefined;
24
+ }, snapshotBroadcaster: HqSnapshotBroadcaster, transcripts: Map<string, TranscriptRing>, agentMessages: Map<string, HqTranscriptEntry[]>, persistence: HqPersistence | undefined, persistEvent: (event: HqEventEnvelope) => void): void;
25
+ //# sourceMappingURL=ws-client-events.d.ts.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * HQ server — Leader tracking, promotion, and leader loss detection.
3
+ *
4
+ * @module hq-server/ws-leader
5
+ */
6
+ import type { HqEventEnvelope, HqPeerRehydrateReason, HqPersistence } from '@wrongstack/core/hq';
7
+ import type { WebSocket } from 'ws';
8
+ import type { ConnectedClient } from './types.js';
9
+ /**
10
+ * Leader tracking: the project leader is the unique client with
11
+ * `control.receive` capability. The first client to register with that
12
+ * capability wins (`isLeader = true`); subsequent clients with the same
13
+ * capability are followers (`isLeader = false`). When the leader's
14
+ * connection is lost, the next-most-recent client with `control.receive`
15
+ * is promoted via `promoteNewLeader` (see `detectLeaderLoss`).
16
+ */
17
+ export declare function computeIsLeader(clients: Map<WebSocket, ConnectedClient>, projectId: string, acceptedCapabilities: readonly string[]): boolean;
18
+ /**
19
+ * After a leader is removed, promote the most recent surviving client with
20
+ * `control.receive` to leader. Returns the promoted client id, or null if
21
+ * no surviving client has `control.receive`.
22
+ */
23
+ export declare function promoteNewLeader(clients: Map<WebSocket, ConnectedClient>, projectId: string, excludeWs: WebSocket, excludeClient: ConnectedClient): string | null;
24
+ /**
25
+ * Detect leader loss and emit the appropriate envelope. Currently wired
26
+ * on `client.closed` (graceful close) and the two heartbeat-timeout sites
27
+ * in `hq-server.ts`. An `auth-revoked` call site is a planned follow-up;
28
+ * the `reason` parameter already accepts it.
29
+ *
30
+ * Returns `true` when a leader-loss event was actually emitted. Note:
31
+ * the dedup gate (`PEER_DEDUP_WINDOW_MS`) suppresses re-emit for the same
32
+ * `(projectId, previousLeaderHandle)` and is intentionally NOT considered
33
+ * a "not the leader" outcome — the lost client is still the leader; the
34
+ * function returns `false` purely because there is nothing new to broadcast.
35
+ * Callers that need to log the loss should consult `lostClient.isLeader`
36
+ * separately.
37
+ *
38
+ * Dedup: a `peer.rehydrate` for the same `(projectId, previousLeaderHandle)`
39
+ * is suppressed if one was emitted within `PEER_DEDUP_WINDOW_MS` (1 h).
40
+ * `peer.lost` is not deduped — a project losing its last surviving client
41
+ * is significant enough to emit every time the topology changes.
42
+ */
43
+ export declare function detectLeaderLoss(lostClient: ConnectedClient, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, reason: HqPeerRehydrateReason, eventSink?: {
44
+ eventLog: HqEventEnvelope[];
45
+ persistence?: HqPersistence | undefined;
46
+ }): boolean;
47
+ //# sourceMappingURL=ws-leader.d.ts.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * HQ server — Client resume protocol handler.
3
+ *
4
+ * @module hq-server/ws-resume
5
+ */
6
+ import type { HqClientResumeMessage, HqEventEnvelope } from '@wrongstack/core/hq';
7
+ import type { WebSocket } from 'ws';
8
+ import type { ConnectedClient, HqSnapshotBroadcaster } from './types.js';
9
+ export declare function browserResumeClient(ws: WebSocket, clientId: string): Map<WebSocket, ConnectedClient>;
10
+ /**
11
+ * Server-side handler for `client.resume` frames.
12
+ *
13
+ * Wired from `handleClient` when the client frame type is `client.resume`.
14
+ * The handler:
15
+ * - Validates identity (clientId/projectId match against the WS).
16
+ * - Updates `client.lastSeenAt`.
17
+ * - Computes the cursor (what the client has received).
18
+ * - Filters `eventLog` for envelopes with `clientId === client.clientId && env.seq > cursor`.
19
+ * - Checks the log cannot serve the gap (log_unavailable).
20
+ * - Checks the gap is fresh (last_seen_too_old).
21
+ * - Checks the gap is not too large (gap_too_large).
22
+ * - Sends `hq.resume_gap` on success.
23
+ *
24
+ * Exported for direct unit testing in `tests/hq-resume-handler.test.ts`.
25
+ */
26
+ export declare function handleClientResume(ws: WebSocket, clients: Map<WebSocket, ConnectedClient>, eventLog: HqEventEnvelope[], frame: HqClientResumeMessage, snapshotBroadcaster?: HqSnapshotBroadcaster): void;
27
+ //# sourceMappingURL=ws-resume.d.ts.map
@@ -3,79 +3,12 @@
3
3
  *
4
4
  * @module hq-server/ws
5
5
  */
6
- import type { HqClientResumeMessage, HqCommandAuditLog, HqEventEnvelope, HqPeerRehydrateReason, HqPersistence, HqRedactionPolicy, HqToken, HqTranscriptEntry } from '@wrongstack/core/hq';
6
+ import type { HqCommandAuditLog, HqEventEnvelope, HqPersistence, HqRedactionPolicy, HqToken, HqTranscriptEntry } from '@wrongstack/core/hq';
7
7
  import { WebSocket } from 'ws';
8
8
  import type { ConnectedClient, HqSnapshotBroadcaster, TranscriptRing } from './types.js';
9
- /**
10
- * Detect leader loss and emit the appropriate envelope. Currently wired
11
- * on `client.closed` (graceful close) and the two heartbeat-timeout sites
12
- * in `hq-server.ts`. An `auth-revoked` call site is a planned follow-up;
13
- * the `reason` parameter already accepts it.
14
- *
15
- * Returns `true` when a leader-loss event was actually emitted. Note:
16
- * the dedup gate (`PEER_DEDUP_WINDOW_MS`) suppresses re-emit for the same
17
- * `(projectId, previousLeaderHandle)` and is intentionally NOT considered
18
- * a "not the leader" outcome — the lost client is still the leader; the
19
- * function returns `false` purely because there is nothing new to broadcast.
20
- * Callers that need to log the loss should consult `lostClient.isLeader`
21
- * separately.
22
- *
23
- * Dedup: a `peer.rehydrate` for the same `(projectId, previousLeaderHandle)`
24
- * is suppressed if one was emitted within `PEER_DEDUP_WINDOW_MS` (1 h).
25
- * `peer.lost` is not deduped — a project losing its last surviving client
26
- * is significant enough to emit every time the topology changes.
27
- */
28
- export declare function detectLeaderLoss(lostClient: ConnectedClient, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, reason: HqPeerRehydrateReason, eventSink?: {
29
- eventLog: HqEventEnvelope[];
30
- persistence?: HqPersistence | undefined;
31
- }): boolean;
32
- /**
33
- * Server-side fanout of a `hq.kanban_snapshot` envelope after a merge.
34
- *
35
- * C2 — broadcasts the post-merge `delta` payload to every client whose
36
- * `projectId` matches `projectId`.
37
- *
38
- * BROWSERS ARE DELIBERATELY NOT NOTIFIED. They used to be — unscoped, so
39
- * every browser received every project's deltas — on the strength of a
40
- * comment claiming "the browser-side projection filters by project when it
41
- * applies the snapshot". No such projection exists: `hq.kanban_snapshot` is
42
- * absent from `HqBrowserMessage` and from the SPA's dispatch chain, so
43
- * `HqWsClient` parsed each frame and dropped it. The board view learns about
44
- * changes from the `kanban.snapshot` HQ *event* and refetches over HTTP
45
- * (`views/kanban.tsx`), which is why the drift was invisible.
46
- *
47
- * `browsers` stays in the signature: the fan-out is the natural place to wire
48
- * a real browser consumer, and the parity test in
49
- * `tests/hq-browser-protocol-parity.test.ts` now fails the moment a
50
- * browser-bound frame has no handler.
51
- *
52
- * The `delta` payload is the same touched-only payload the merge handler
53
- * builds (full-set broadcasts drive V8 heap exhaustion on long-lived
54
- * boards — see the merge handler's comment).
55
- *
56
- * Exported for direct unit testing in `tests/hq-kanban-fanout.test.ts`.
57
- */
58
- export declare function fanoutKanbanDelta(message: string, clients: Map<WebSocket, ConnectedClient>, _browsers: Set<WebSocket>, projectId: string): {
59
- clientsNotified: number;
60
- browsersNotified: number;
61
- };
62
- /**
63
- * Server-side handler for `client.resume` frames.
64
- *
65
- * Wired from `handleClient` (line 511) when the client frame type is
66
- * `client.resume`. The handler:
67
- * - Validates identity (clientId/projectId match against the WS).
68
- * - Updates `client.lastSeenAt`.
69
- * - Computes the cursor (what the client has received).
70
- * - Filters `eventLog` for envelopes with `clientId === client.clientId && env.seq > cursor`.
71
- * - Checks the log cannot serve the gap (log_unavailable).
72
- * - Checks the gap is fresh (last_seen_too_old).
73
- * - Checks the gap is not too large (gap_too_large).
74
- * - Sends `hq.resume_gap` on success.
75
- *
76
- * Exported for direct unit testing in `tests/hq-resume-handler.test.ts`.
77
- */
78
- export declare function handleClientResume(ws: WebSocket, clients: Map<WebSocket, ConnectedClient>, eventLog: HqEventEnvelope[], frame: HqClientResumeMessage, snapshotBroadcaster?: HqSnapshotBroadcaster): void;
9
+ export { detectLeaderLoss } from './ws-leader.js';
10
+ export { fanoutKanbanDelta } from './ws-client-events.js';
11
+ export { handleClientResume } from './ws-resume.js';
79
12
  export declare function handleBrowser(ws: WebSocket, snapshotBroadcaster: HqSnapshotBroadcaster, browsers: Set<WebSocket>, eventLog: HqEventEnvelope[]): void;
80
13
  export declare function handleClient(ws: WebSocket, clients: Map<WebSocket, ConnectedClient>, browsers: Set<WebSocket>, eventLog: HqEventEnvelope[], auth: {
81
14
  token?: HqToken | undefined;
@@ -15,7 +15,7 @@ import {
15
15
  readLocalSubagentTranscript,
16
16
  sanitizeApiError,
17
17
  startHqServer
18
- } from "./chunk-RHLQT3EV.js";
18
+ } from "./chunk-CB35UVXO.js";
19
19
  import "./chunk-KE7E7DPX.js";
20
20
  import "./chunk-Q5GTM25S.js";
21
21
  import "./chunk-7OCVIDC7.js";
@@ -37,4 +37,4 @@ export {
37
37
  sanitizeApiError,
38
38
  startHqServer
39
39
  };
40
- //# sourceMappingURL=hq-server-IVZLVCFV.js.map
40
+ //# sourceMappingURL=hq-server-PBRU275L.js.map
@@ -24,7 +24,6 @@ export declare const BROWSER_HEARTBEAT_INTERVAL_MS = 15000;
24
24
  export declare const SESSION_SNAPSHOT_TTL_MS = 30000;
25
25
  export { agentMessageToEntry, agentRingKey, decodePathSegment, readLocalSubagentTranscript, sanitizeApiError, };
26
26
  export interface HqServerOptions {
27
- /** Policy authority for control-plane command enqueue decisions. */
28
27
  trustBoundary?: TrustBoundary | undefined;
29
28
  host?: string;
30
29
  port?: number;
@@ -39,45 +38,8 @@ export interface HqServerOptions {
39
38
  allowInsecureOpen?: boolean;
40
39
  secureCookies?: boolean;
41
40
  requireBrowserAuth?: boolean;
42
- /**
43
- * Trust `Origin: file://` on the HTTP and WebSocket surfaces, for serving the
44
- * HQ dashboard from a local file in air-gapped use. Default false.
45
- *
46
- * Off by default because the Host check does not contain a `file:` page — it
47
- * can aim at the real HQ authority — so trusting it unconditionally let any
48
- * locally-opened HTML file clear HQ's only cross-origin control (WS-081).
49
- * Enable only when you actually serve the dashboard that way.
50
- */
51
41
  allowFileOrigin?: boolean;
52
- /**
53
- * Optional time-to-live (milliseconds) stamped on the first-run browser
54
- * and client tokens minted on a brand-new HQ data directory. Existing
55
- * auth.json files are not modified. When set, tokens carry an `expiresAt`
56
- * and the server refuses them past that timestamp; default is no expiry.
57
- *
58
- * Useful for short-lived deployments (CI, ephemeral relays) where a leaked
59
- * first-run token should not outlive the deployment window. Set via the
60
- * `--hq-token-ttl-ms` CLI flag.
61
- */
62
42
  tokenTtlMs?: number;
63
- /**
64
- * How many trusted reverse proxies sit between a browser and this server.
65
- * Controls which address the login backoff keys on (WS-106).
66
- *
67
- * Default `0`: `X-Forwarded-For` is ignored entirely and the socket peer is
68
- * used — correct for a direct bind, and the only safe default, because the
69
- * header is client-supplied and honouring it unasked would let an attacker
70
- * mint a fresh rate-limit identity per request.
71
- *
72
- * Set it to the real hop count when HQ runs behind a tunnel or relay
73
- * (`--hq-public-url`, `--hq-require-browser-auth`). Without it every request
74
- * arrives from the tunnel's own address, so all users share one backoff
75
- * bucket: one attacker locks out everyone, and a distributed attacker is not
76
- * limited per-source at all. `1` covers the common single-tunnel case.
77
- *
78
- * Only the rightmost `n` forwarded entries are ever read — see
79
- * `hq-server/client-address.ts`. Set via `--hq-trusted-proxy-hops`.
80
- */
81
43
  trustedProxyHops?: number;
82
44
  }
83
45
  export interface HqStartupConnectionInfo {