signalk-questdb 1.7.0 → 1.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/CLAUDE.md CHANGED
@@ -36,6 +36,8 @@ Both build outputs are shipped in the npm package; `prepublishOnly` rebuilds the
36
36
  - **v2** (`src/history-v2.ts`) — `app.registerHistoryApiProvider()`, REST under `/signalk/v2/api/history/`. Aggregates map directly to QuestDB SQL except `sma`/`ema` (computed client-side).
37
37
  - **v1** (`src/history-v1.ts`) — `app.registerHistoryProvider()`, WebSocket playback with chunked reads.
38
38
  - Retention (`src/retention.ts`) drops old daily partitions on a timer.
39
+ - Startup restore (`src/restore.ts`) optionally replays each vessel's last recorded position back into the live model after a server restart.
40
+ - Console proxy (`src/console-proxy.ts`) reverse-proxies QuestDB's own web console so the embeddable webapp (`public/index.html`) can host it in the admin UI.
39
41
 
40
42
  ### Container integration
41
43
 
@@ -53,6 +55,8 @@ The package is the unscoped `typebox` (1.x), not `@sinclair/typebox` (which stop
53
55
 
54
56
  All extra plugin endpoints live under `/plugins/signalk-questdb/api/` and are wired in `src/index.ts` via the `IRouter` Signal K passes to `registerWithRouter`. The `/query` endpoint is gated by `isReadOnlySQL` in `src/query-client.ts` — DDL/DML must remain blocked.
55
57
 
58
+ ⚠️ **`/console` is deliberately NOT gated that way.** It proxies QuestDB's own console, which is a full SQL client and can drop tables. It is safe only because signalk-server gives plugin routes an **admin-only default**, and routes are downgraded to readwrite/readonly _only_ by registering them through `router.access(...)`. Never wrap the console mount in `.access()` — doing so hands non-admins the ability to delete recorded history. The mount is also gated on the `enableConsole` config flag, so turning it off removes the route rather than leaving it to refuse.
59
+
56
60
  Their wire shapes live in `src/api-contract.ts` and are shared by both surfaces: handlers assert responses with `satisfies`, and the panel casts fetch results to the same types. Add or rename a response field there, not inline — that is what stops the server and panel drifting apart silently.
57
61
 
58
62
  ### Tests
package/README.md CHANGED
@@ -20,6 +20,7 @@ Stores all vessel data in QuestDB running as a managed container (via [signalk-c
20
20
  - **CSV export** -- download historical data via REST endpoint
21
21
  - **InfluxDB migration** -- auto-detect InfluxDB 1.x/2.x on localhost or remote URL
22
22
  - **One-click updates** -- check for new QuestDB releases and update from the config panel
23
+ - **Console webapp** -- QuestDB's own SQL console embedded in the Signal K admin UI (admin only)
23
24
  - **Config panel** -- status dashboard with row counts, version picker, update check, collapsible compression/migration/export sections
24
25
  - **SQL injection protection** -- strict input validation on all query endpoints
25
26
  - **Container lifecycle** -- container stops when plugin is disabled, starts on enable
@@ -32,7 +33,7 @@ The plugin embeds a React config panel in the Signal K Admin UI showing:
32
33
  - **Update check** -- compares running version against latest GitHub release, one-click update
33
34
  - **Image Version** -- dropdown with latest, pre-releases, and last 3 stable releases
34
35
  - **Connection** -- managed container toggle, host/ports, PostgreSQL port for Grafana
35
- - **Recording** -- record self, record AIS targets, retention days
36
+ - **Recording** -- record self, record AIS targets, startup restore, console webapp, retention days
36
37
  - **Path filtering** (collapsible) -- exclude or include-only paths with glob patterns (e.g. exclude `navigation.position`)
37
38
  - **Compression** (collapsible) -- LZ4/ZSTD codec selection for on-disk storage
38
39
  - **InfluxDB Migration** (collapsible) -- auto-detect with manual URL for remote instances
@@ -129,6 +130,7 @@ wants to slice it into kopia-dedup-friendly shards. Allowed tables:
129
130
  | Record AIS targets | `true` | Record other vessels |
130
131
  | Restore vessels on startup | `false` | Replay each vessel's last recorded position after a restart (see Startup restore) |
131
132
  | Restore max age (minutes) | `9` | Only replay values recorded within this window |
133
+ | QuestDB console webapp | `true` | Serve QuestDB's console in the Signal K admin UI, admin only (see Console webapp) |
132
134
  | Retention (days) | `0` | Auto-delete old partitions (0 = keep forever) |
133
135
  | Path filter mode | `exclude` | `exclude` matching paths, or `include` only matching paths |
134
136
  | Path filter paths | _(empty)_ | Glob patterns, one per line (e.g. `navigation.position`); empty = record everything |
@@ -137,6 +139,37 @@ wants to slice it into kopia-dedup-friendly shards. Allowed tables:
137
139
  | Container network | `sk-network` | Shared network for QuestDB (only applied when binding to 0.0.0.0) |
138
140
  | Bind to 0.0.0.0 | `false` | Expose QuestDB's ports on the LAN (see Connectivity below) |
139
141
 
142
+ ## Console webapp
143
+
144
+ QuestDB ships its own web console — a full SQL workbench with schema browsing,
145
+ query history and CSV import. It normally lives on QuestDB's HTTP port (9000),
146
+ which is bound to loopback unless you turn on **Bind to 0.0.0.0**, so from any
147
+ other device on the boat it is effectively unreachable.
148
+
149
+ With **QuestDB console webapp** enabled (the default) the plugin appears in the
150
+ Signal K webapp list and serves that console inside the admin UI, with Signal
151
+ K's own navigation panel still in place.
152
+
153
+ **It is admin only.** The console is proxied at
154
+ `/plugins/signalk-questdb/console/`, and Signal K gives plugin routes an
155
+ admin-only default — the plugin deliberately does not relax that. A non-admin
156
+ who opens the webapp sees the page frame and an authorization error, never the
157
+ console.
158
+
159
+ **It can modify data.** Unlike the read-only `/query` endpoint, which rejects
160
+ anything that is not a `SELECT`, the console is a real SQL client: it can drop
161
+ tables and delete recorded history. That is the point of having it, and it is
162
+ what an administrator would use it for — but it is worth knowing before handing
163
+ someone an admin account.
164
+
165
+ If you would rather not serve it at all, switch the option off and the route is
166
+ not registered.
167
+
168
+ > Note this is _safer_ than the alternative it replaces. Reaching the console
169
+ > from another device previously meant enabling **Bind to 0.0.0.0**, which
170
+ > publishes an unauthenticated SQL endpoint on your network. The webapp route
171
+ > requires a Signal K admin session.
172
+
140
173
  ## Startup restore
141
174
 
142
175
  Signal K's data model lives in memory only. After a server restart every vessel
@@ -19,6 +19,7 @@ export declare const ConfigSchema: Type.TObject<{
19
19
  recordOthers: Type.TBoolean;
20
20
  restoreOnStart: Type.TBoolean;
21
21
  restoreMaxAgeMinutes: Type.TNumber;
22
+ enableConsole: Type.TBoolean;
22
23
  retentionDays: Type.TNumber;
23
24
  networkName: Type.TString;
24
25
  exposeToContainers: Type.TBoolean;
@@ -86,6 +86,11 @@ export const ConfigSchema = Type.Object({
86
86
  title: "Restore max age (minutes)",
87
87
  description: "Only replay values recorded within this many minutes. Matches Freeboard's default AIS expiry (9 minutes), so a restored target is one that would still have been on the chart. Raising this puts progressively staler positions on the chart.",
88
88
  }),
89
+ enableConsole: Type.Boolean({
90
+ default: true,
91
+ title: "QuestDB console webapp",
92
+ description: "Serve QuestDB's own web console inside the Signal K admin UI (admin only). The console is a full SQL client — unlike the read-only query endpoint it can modify and delete recorded data. Turn this off to remove the route entirely.",
93
+ }),
89
94
  retentionDays: Type.Number({
90
95
  default: 0,
91
96
  title: "Retention (days, 0 = keep forever)",
@@ -142,6 +147,10 @@ export function normalizeConfig(config) {
142
147
  restoreMaxAgeMinutes: config.restoreMaxAgeMinutes && config.restoreMaxAgeMinutes > 0
143
148
  ? config.restoreMaxAgeMinutes
144
149
  : 9,
150
+ // On unless explicitly disabled, matching the recording toggles rather
151
+ // than restoreOnStart: the console only ever does what an authenticated
152
+ // admin types, so a missing key should not hide the tool.
153
+ enableConsole: config.enableConsole !== false,
145
154
  // Missing resource limits get the schema defaults. Config-panel saves
146
155
  // used to drop these keys entirely (issue #98), which ran QuestDB with
147
156
  // NO memory cap instead of the promised 768m — this backfill repairs
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ /**
3
+ * Reverse proxy for QuestDB's own web console.
4
+ *
5
+ * The console is a separate app served by the QuestDB container on its HTTP
6
+ * port. Rather than rebuild it, this forwards requests to it so the Signal K
7
+ * admin UI can embed it — keeping Signal K's nav panel in place instead of
8
+ * sending the user off to `:9000`, which is unreachable from another device
9
+ * unless the operator publishes the port on the LAN.
10
+ *
11
+ * Plain `node:http` piping rather than a proxy library: the plugin's dependency
12
+ * tree is deliberately lean, and what is needed here (method, path, query,
13
+ * headers, body, streamed response) is a few lines.
14
+ *
15
+ * Path handling is why this works without patching QuestDB's bundle: the
16
+ * console requests its assets and its API relatively (`./assets/…`, `exec?…`),
17
+ * so serving it under a prefix needs no rewriting. If a future QuestDB image
18
+ * switches to absolute paths that breaks visibly as 404s — see the plan's
19
+ * manual-verification step — rather than silently.
20
+ *
21
+ * SECURITY: the console is a full SQL client. Unlike `/api/query`, which is
22
+ * gated by isReadOnlySQL, it can drop tables. It is reachable only because
23
+ * routes registered directly on a plugin router are admin-only by default in
24
+ * signalk-server. Do NOT register the mount through `router.access(...)` —
25
+ * that would downgrade it to readwrite/readonly and hand non-admins a way to
26
+ * delete the vessel's recorded history.
27
+ */
28
+ export interface ConsoleProxyDeps {
29
+ /** Base URL of QuestDB's HTTP interface, e.g. `http://127.0.0.1:9000`. */
30
+ baseUrl: () => string | null;
31
+ debug: (msg: string) => void;
32
+ }
33
+ export declare function createConsoleProxy(deps: ConsoleProxyDeps): (req: IncomingMessage, res: ServerResponse) => void;
@@ -0,0 +1,162 @@
1
+ import http from "node:http";
2
+ // Hop-by-hop headers must not be forwarded (RFC 7230 §6.1). `host` is dropped
3
+ // separately because it has to name the upstream, not the Signal K server.
4
+ const HOP_BY_HOP = new Set([
5
+ "connection",
6
+ "keep-alive",
7
+ "proxy-authenticate",
8
+ "proxy-authorization",
9
+ "te",
10
+ "trailer",
11
+ "transfer-encoding",
12
+ "upgrade",
13
+ ]);
14
+ /**
15
+ * Header names `Connection` nominates for removal. RFC 7230 §6.1 lets a hop
16
+ * list additional single-hop headers there, so the fixed set above is not the
17
+ * whole story — forwarding a nominated header leaks connection-scoped state
18
+ * across the hop.
19
+ */
20
+ function connectionNominated(headers) {
21
+ const raw = Array.isArray(headers.connection)
22
+ ? headers.connection.join(",")
23
+ : (headers.connection ?? "");
24
+ return new Set(raw
25
+ .split(",")
26
+ .map((v) => v.trim().toLowerCase())
27
+ .filter(Boolean));
28
+ }
29
+ function stripHopByHop(headers) {
30
+ const nominated = connectionNominated(headers);
31
+ const out = {};
32
+ for (const [key, value] of Object.entries(headers)) {
33
+ if (value === undefined)
34
+ continue;
35
+ const lower = key.toLowerCase();
36
+ if (HOP_BY_HOP.has(lower) || nominated.has(lower))
37
+ continue;
38
+ out[key] = value;
39
+ }
40
+ return out;
41
+ }
42
+ function forwardableHeaders(headers) {
43
+ const out = stripHopByHop(headers);
44
+ // `host` must name the upstream, not the Signal K server.
45
+ delete out.host;
46
+ // Signal K's session cookie is not QuestDB's business, and forwarding it
47
+ // would leak an admin credential to a process that has no use for it.
48
+ delete out.cookie;
49
+ delete out.authorization;
50
+ return out;
51
+ }
52
+ /**
53
+ * Response headers must be filtered too. The upstream's own hop-by-hop
54
+ * headers describe ITS connection to us, not ours to the browser; relaying
55
+ * them lets QuestDB's framing decisions override the ones Node is making for
56
+ * the client's connection.
57
+ */
58
+ function returnableHeaders(headers) {
59
+ const out = stripHopByHop(headers);
60
+ // The console shares the Signal K origin, so a proxied Set-Cookie would be
61
+ // applied to it — letting a misconfigured or compromised QuestDB write
62
+ // cookies that collide with Signal K's own session handling. Nothing is
63
+ // lost by dropping it: the request side already strips `cookie`, so no
64
+ // QuestDB cookie could ever have completed a round trip anyway.
65
+ delete out["set-cookie"];
66
+ return out;
67
+ }
68
+ export function createConsoleProxy(deps) {
69
+ return function consoleProxy(req, res) {
70
+ const base = deps.baseUrl();
71
+ if (!base) {
72
+ res.writeHead(503, { "content-type": "text/plain" });
73
+ res.end("QuestDB is not running.");
74
+ return;
75
+ }
76
+ let target;
77
+ try {
78
+ // `req.url` here is already relative to the mount point (Express strips
79
+ // the matched prefix for `router.use`), so "/" is the console root.
80
+ target = new URL(req.url ?? "/", base);
81
+ }
82
+ catch {
83
+ res.writeHead(400, { "content-type": "text/plain" });
84
+ res.end("Bad request path.");
85
+ return;
86
+ }
87
+ // Confine the proxy to the resolved QuestDB origin. `new URL` would
88
+ // otherwise let a path like `//evil.example.com/` re-target the request,
89
+ // turning an admin-only route into an open relay.
90
+ const origin = new URL(base);
91
+ if (target.origin !== origin.origin) {
92
+ res.writeHead(400, { "content-type": "text/plain" });
93
+ res.end("Bad request path.");
94
+ return;
95
+ }
96
+ const upstream = http.request({
97
+ protocol: origin.protocol,
98
+ hostname: origin.hostname,
99
+ port: origin.port,
100
+ method: req.method,
101
+ path: target.pathname + target.search,
102
+ headers: forwardableHeaders(req.headers),
103
+ }, (proxied) => {
104
+ const headers = returnableHeaders(proxied.headers);
105
+ // The console is served under the Signal K origin, so its scripts run
106
+ // with the admin's session. Signal K's auth cookie is httpOnly, so
107
+ // script cannot read it — but it IS attached to same-origin requests,
108
+ // which would let a tampered console drive Signal K's admin API as the
109
+ // logged-in user. A plugin cannot mint a separate origin, so narrow
110
+ // what the console may do instead.
111
+ //
112
+ // Deliberately NOT `connect-src 'self'`: the console's SQL editor is
113
+ // Monaco, and the bundle carries a jsdelivr loader path for it. Static
114
+ // analysis could not settle whether that path is actually taken, and
115
+ // shipping a policy that blanks out the query editor would be a worse
116
+ // outcome than the risk it mitigates. `form-action` and `base-uri`
117
+ // still block the two cheapest ways to redirect an admin's submission
118
+ // or rewrite relative URLs, and `frame-ancestors` blocks clickjacking
119
+ // from another site.
120
+ //
121
+ // Revisit if QuestDB ever bundles Monaco locally — then `connect-src`
122
+ // and `default-src 'self'` become safe to tighten, which is the real
123
+ // fix for a same-origin console.
124
+ headers["content-security-policy"] =
125
+ "form-action 'self'; frame-ancestors 'self'; base-uri 'self'";
126
+ // The console has no reason to be sniffed into a different type.
127
+ headers["x-content-type-options"] = "nosniff";
128
+ res.writeHead(proxied.statusCode ?? 502, headers);
129
+ // A HEAD response carries no body by definition. QuestDB nonetheless
130
+ // answers HEAD with `405 Transfer-Encoding: chunked` AND a body, which
131
+ // is protocol-illegal; piping that upstream response through leaves
132
+ // the client waiting for chunks that never legally arrive and the
133
+ // connection dies with an empty reply. Terminate the response
134
+ // ourselves and discard whatever the upstream sent.
135
+ if (req.method === "HEAD") {
136
+ proxied.resume();
137
+ res.end();
138
+ return;
139
+ }
140
+ proxied.pipe(res);
141
+ });
142
+ upstream.on("error", (err) => {
143
+ deps.debug(`console proxy error: ${err.message}`);
144
+ if (res.headersSent) {
145
+ res.destroy();
146
+ return;
147
+ }
148
+ // The overwhelmingly common cause is the container being stopped, so say
149
+ // that rather than surfacing an ECONNREFUSED stack to the browser.
150
+ res.writeHead(502, { "content-type": "text/plain" });
151
+ res.end("QuestDB is not reachable. Is the container running?");
152
+ });
153
+ // A client that goes away mid-request must not leave the upstream socket
154
+ // open — the console streams large query results.
155
+ res.on("close", () => {
156
+ if (!upstream.destroyed)
157
+ upstream.destroy();
158
+ });
159
+ req.pipe(upstream);
160
+ };
161
+ }
162
+ //# sourceMappingURL=console-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"console-proxy.js","sourceRoot":"","sources":["../src/console-proxy.ts"],"names":[],"mappings":"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"}
@@ -165,6 +165,23 @@ export function createHistoryProviderV1(queryClient, selfContext, debug) {
165
165
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
166
166
  onChange) {
167
167
  let stopped = false;
168
+ // The pending inter-chunk timer. Tracked so stopping actually CANCELS it:
169
+ // clearing `stopped` alone leaves a scheduled callback holding the event
170
+ // loop open for up to CHUNK_SECONDS (60s at playbackRate 1), which kept
171
+ // the test process alive long past its assertions and tripped the plugin
172
+ // registry's 60s test budget.
173
+ let chunkTimer = null;
174
+ const scheduleChunk = (delayMs) => {
175
+ if (stopped)
176
+ return;
177
+ chunkTimer = setTimeout(() => {
178
+ chunkTimer = null;
179
+ streamChunk();
180
+ }, delayMs);
181
+ // Never let a playback timer be the only thing keeping the process
182
+ // alive — the server owns the lifecycle, not this stream.
183
+ chunkTimer.unref?.();
184
+ };
168
185
  const startTime = validateTimestamp(options.startTime.toISOString());
169
186
  const playbackRate = Math.max(1, options.playbackRate);
170
187
  // Vessel labels for this playback: last-known name per context,
@@ -197,7 +214,7 @@ export function createHistoryProviderV1(queryClient, selfContext, debug) {
197
214
  if (result.dataset.length === 0) {
198
215
  currentTime = chunkEnd;
199
216
  if (!stopped) {
200
- setTimeout(streamChunk, 100);
217
+ scheduleChunk(100);
201
218
  }
202
219
  return;
203
220
  }
@@ -257,30 +274,36 @@ export function createHistoryProviderV1(queryClient, selfContext, debug) {
257
274
  const resumeAt = lastTs > currentTime.getTime() ? lastTs : currentTime.getTime() + 1;
258
275
  currentTime = new Date(Math.min(resumeAt, chunkEnd.getTime()));
259
276
  if (!stopped) {
260
- setTimeout(streamChunk, 0);
277
+ scheduleChunk(0);
261
278
  }
262
279
  return;
263
280
  }
264
281
  currentTime = chunkEnd;
265
282
  const wallDelay = (CHUNK_SECONDS * 1000) / playbackRate;
266
283
  if (!stopped) {
267
- setTimeout(streamChunk, wallDelay);
284
+ scheduleChunk(wallDelay);
268
285
  }
269
286
  }
270
287
  catch (err) {
271
288
  debug(`streamHistory error: ${err instanceof Error ? err.message : String(err)}`);
272
289
  if (!stopped) {
273
- setTimeout(streamChunk, 1000);
290
+ scheduleChunk(1000);
274
291
  }
275
292
  }
276
293
  }
277
294
  streamChunk();
278
- spark.on("end", () => {
279
- stopped = true;
280
- });
281
- return () => {
295
+ // Cancel the pending chunk as well as setting the flag: the flag only
296
+ // stops the NEXT scheduling decision, while an already-scheduled timer
297
+ // keeps the event loop alive until it fires.
298
+ const stop = () => {
282
299
  stopped = true;
300
+ if (chunkTimer) {
301
+ clearTimeout(chunkTimer);
302
+ chunkTimer = null;
303
+ }
283
304
  };
305
+ spark.on("end", stop);
306
+ return stop;
284
307
  }
285
308
  // Snapshot of every path at `date`, for the v1 snapshot API.
286
309
  //
@@ -1 +1 @@
1
- {"version":3,"file":"history-v1.js","sourceRoot":"","sources":["../src/history-v1.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"history-v1.js","sourceRoot":"","sources":["../src/history-v1.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -42,6 +42,7 @@ declare const _default: (app: App) => {
42
42
  recordOthers: import("typebox").TBoolean;
43
43
  restoreOnStart: import("typebox").TBoolean;
44
44
  restoreMaxAgeMinutes: import("typebox").TNumber;
45
+ enableConsole: import("typebox").TBoolean;
45
46
  retentionDays: import("typebox").TNumber;
46
47
  networkName: import("typebox").TString;
47
48
  exposeToContainers: import("typebox").TBoolean;
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { createHistoryProviderV2 } from "./history-v2.js";
6
6
  import { createHistoryProviderV1 } from "./history-v1.js";
7
7
  import { startRetention } from "./retention.js";
8
8
  import { RESTORE_SOURCE, restoreFromHistory } from "./restore.js";
9
+ import { createConsoleProxy } from "./console-proxy.js";
9
10
  import { buildFullExportWhere } from "./full-export-range.js";
10
11
  import { WalMonitor, buildPendingSegmentsSQL, computeSkipPlan, extractApplyError, isPartitionOpenFailure, skipPlansEqual, } from "./wal-monitor.js";
11
12
  import { QUESTDB_INTERNAL_HTTP_PORT, QUESTDB_INTERNAL_ILP_PORT, QUESTDB_INTERNAL_PG_PORT, QUESTDB_ACCESSIBLE_PORTS, resolveManagedEndpoints, resolveLanExposureHost, lanExposureEndpoints, } from "./questdb-endpoint.js";
@@ -236,6 +237,19 @@ export default (app) => {
236
237
  const port = currentConfig?.questdbHttpPort ?? QUESTDB_INTERNAL_HTTP_PORT;
237
238
  return `http://${host}:${port}`;
238
239
  }
240
+ // Built once; it resolves the upstream lazily on every request so a
241
+ // container restart (which can change the resolved address) is picked up
242
+ // without re-registering the route.
243
+ //
244
+ // Gated on config rather than on `questdbEndpoints`: that is only populated
245
+ // on the managed-container path, so keying off it made the console return
246
+ // 503 forever in external mode, where the host/port come from config and
247
+ // questdbHttpBaseUrl() already falls back to them. Null only before any
248
+ // config has loaded, which is the one moment there is nothing to point at.
249
+ const consoleProxy = createConsoleProxy({
250
+ baseUrl: () => (currentConfig ? questdbHttpBaseUrl() : null),
251
+ debug: (msg) => app.debug(msg),
252
+ });
239
253
  /**
240
254
  * Compute the bind-mount source for QuestDB's /var/lib/questdb volume.
241
255
  *
@@ -1016,6 +1030,27 @@ export default (app) => {
1016
1030
  currentConfig = null;
1017
1031
  },
1018
1032
  registerWithRouter(router) {
1033
+ // QuestDB's own console, proxied so the Signal K admin UI can embed it.
1034
+ //
1035
+ // Registered DIRECTLY on the router and NOT through router.access(...):
1036
+ // plugin routes default to admin-only in signalk-server, and only
1037
+ // .access() downgrades them. The console is a full SQL client that can
1038
+ // drop tables, so downgrading it would hand a non-admin the ability to
1039
+ // delete the vessel's recorded history. Leave this as it is.
1040
+ //
1041
+ // The enableConsole check lives INSIDE the handler, not around the
1042
+ // mount: registerWithRouter runs at plugin registration, before start()
1043
+ // has loaded any configuration, so `currentConfig` is still null here
1044
+ // and gating the mount would ignore a saved opt-out entirely. Checking
1045
+ // per-request also means toggling the option takes effect on plugin
1046
+ // restart without the route going stale.
1047
+ router.use("/console", (req, res, next) => {
1048
+ if (currentConfig?.enableConsole === false) {
1049
+ next();
1050
+ return;
1051
+ }
1052
+ consoleProxy(req, res);
1053
+ });
1019
1054
  router.get("/api/status", async (_req, res) => {
1020
1055
  try {
1021
1056
  if (!queryClient) {