@suveren/gateway 0.19.0 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -1
- package/bin/suveren-gateway.js +199 -12
- package/dist/control-plane/index.mjs +18 -0
- package/dist/ui/assets/{index-DeQPO4Sj.js → index-DF3kB2R3.js} +14 -14
- package/dist/ui/index.html +1 -1
- package/lib/install-settings.d.mts +41 -0
- package/lib/install-settings.mjs +318 -0
- package/lib/policy.d.mts +6 -1
- package/lib/policy.mjs +48 -4
- package/node_modules/@hap/core/dist/index.d.mts +13 -0
- package/node_modules/@hap/core/dist/index.d.ts +13 -0
- package/node_modules/@hap/core/package.json +1 -1
- package/node_modules/@hap/core/src/types.ts +13 -0
- package/package.json +1 -1
- package/server.js +20 -4
package/README.md
CHANGED
|
@@ -142,7 +142,22 @@ For laptops managed by a company's IT: no admin rights, no Node.js and no access
|
|
|
142
142
|
|
|
143
143
|
**The installer is not signed by us.** IT checks where it comes from (`sha256sum -c SHA256SUMS` and `gh attestation verify <file> -R suverenai/suveren-gateway`, which shows the exact commit it was built from), signs it, and distributes it, e.g. with Intune. IT can preset the Authority Server, simulation mode, proxy and certificate centrally; the employee cannot change them, and updates come from IT. Step by step: [Windows IT guide](docs/windows-it-guide.md) · settings: [Managed settings](docs/managed-settings.md).
|
|
144
144
|
|
|
145
|
-
Open `http://localhost:3400` (Start menu: *Suveren Gateway*). The MCP server is at `http://localhost:3430`.
|
|
145
|
+
Open `http://localhost:3400` (Start menu: *Suveren Gateway*). The MCP server is at `http://localhost:3430`. Other ports or another data folder: `msiexec /i suveren-gateway.msi PORT=3500 MCP_PORT=3530 DATA_DIR="D:\Suveren"`, or the "Gateway settings" page of a double-click install — see the [IT guide](docs/windows-it-guide.md#port-and-data-folder-optional).
|
|
146
|
+
|
|
147
|
+
### Ports and data folder
|
|
148
|
+
|
|
149
|
+
npm and the Windows installer default to port 3400 (app), 3430 (AI assistants) and the data folder `~/.suveren`. To change them for good — kept across restarts, upgrades and autostart:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
suveren-gateway stop # if you started it with --detach
|
|
153
|
+
suveren-gateway config set port 3500
|
|
154
|
+
suveren-gateway config set mcp-port 3530
|
|
155
|
+
suveren-gateway config set data-dir /Volumes/Data/suveren
|
|
156
|
+
suveren-gateway config get # shows each value and where it comes from
|
|
157
|
+
suveren-gateway config unset port # back to the default
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Changing the data folder **does not move** existing data — copy the old folder yourself first if you want to keep it. After changing `mcp-port`, update the MCP address in every AI assistant you connected. Environment variables (`SUVEREN_CP_PORT`, `SUVEREN_MCP_PORT`, `SUVEREN_DATA_DIR`) still win over saved values; IT policy wins over both ([Managed settings](docs/managed-settings.md)).
|
|
146
161
|
|
|
147
162
|
### Connecting an MCP client
|
|
148
163
|
|
package/bin/suveren-gateway.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { spawn, spawnSync } from 'node:child_process';
|
|
14
14
|
import { createConnection } from 'node:net';
|
|
15
|
-
import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync, unlinkSync, statSync } from 'node:fs';
|
|
15
|
+
import { existsSync, mkdirSync, openSync, readFileSync, readdirSync, writeFileSync, unlinkSync, statSync } from 'node:fs';
|
|
16
16
|
import { homedir, platform, userInfo } from 'node:os';
|
|
17
17
|
import { dirname, join, resolve } from 'node:path';
|
|
18
18
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -22,6 +22,7 @@ import { createInterface } from 'node:readline/promises';
|
|
|
22
22
|
import { readPairing as readAsPairing, recordTlsPin, formatFingerprint, normalizeFingerprint } from '../lib/as-pairing.mjs';
|
|
23
23
|
import { unsupportedNodeReason } from '../lib/node-version.mjs';
|
|
24
24
|
import { readPolicy, isPolicyLocked } from '../lib/policy.mjs';
|
|
25
|
+
import { resolveInstallSettings, describeSource, installSettingsLocation, validatePort, validateDataDir, writeInstallSettings } from '../lib/install-settings.mjs';
|
|
25
26
|
|
|
26
27
|
// Before any command: on an unsupported Node the connectors cannot run (see
|
|
27
28
|
// lib/node-version.mjs), so refuse with the reason instead of starting.
|
|
@@ -35,11 +36,29 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
35
36
|
const PKG_ROOT = resolve(__dirname, '..');
|
|
36
37
|
const SERVER_ENTRY = join(PKG_ROOT, 'server.js');
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
// Ports + data folder: IT policy > env > saved install setting > default
|
|
40
|
+
// (lib/install-settings.mjs — the same resolver server.js uses, so the CLI
|
|
41
|
+
// and the running gateway always agree). An invalid value stops every
|
|
42
|
+
// command except `config` and `help`, which stay usable so the person can
|
|
43
|
+
// see and fix it.
|
|
44
|
+
const COMMAND = process.argv[2] ?? 'help';
|
|
45
|
+
const TOLERANT = ['config', 'help', '--help', '-h'].includes(COMMAND);
|
|
46
|
+
let INSTALL;
|
|
47
|
+
try {
|
|
48
|
+
INSTALL = resolveInstallSettings({ tolerant: TOLERANT });
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
51
|
+
console.error('Fix it with `suveren-gateway config set …` — see `suveren-gateway config help`.');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
for (const warning of INSTALL.errors) console.error(`Warning: ${warning}`);
|
|
55
|
+
|
|
56
|
+
const DATA_DIR = INSTALL.dataDir;
|
|
39
57
|
const PID_FILE = join(DATA_DIR, 'gateway.pid');
|
|
40
58
|
const LOG_FILE = join(DATA_DIR, 'gateway.log');
|
|
41
59
|
|
|
42
|
-
const SUVEREN_PORT =
|
|
60
|
+
const SUVEREN_PORT = String(INSTALL.cpPort);
|
|
61
|
+
const MCP_PORT = String(INSTALL.mcpPort);
|
|
43
62
|
|
|
44
63
|
/** Version of THIS CLI (the binary on disk). Compared against the
|
|
45
64
|
* running gateway's version inside `status` so users see a mismatch
|
|
@@ -267,11 +286,21 @@ async function start(args) {
|
|
|
267
286
|
console.error(`another terminal, which leaves no PID file for this CLI to find.`);
|
|
268
287
|
console.error(``);
|
|
269
288
|
console.error(` Check what it is: suveren-gateway status`);
|
|
270
|
-
console.error(` Or use another port:
|
|
289
|
+
console.error(` Or use another port: suveren-gateway config set port 3410`);
|
|
271
290
|
}
|
|
272
291
|
process.exit(1);
|
|
273
292
|
}
|
|
274
293
|
|
|
294
|
+
// The MCP port too: before this check, a taken MCP port let the gateway
|
|
295
|
+
// "start", then its MCP server died on EADDRINUSE and took the rest down
|
|
296
|
+
// with it — reported only in the log.
|
|
297
|
+
if (await isPortListening(MCP_PORT)) {
|
|
298
|
+
console.error(`Port ${MCP_PORT} (for AI assistants, MCP) is already in use.`);
|
|
299
|
+
console.error(``);
|
|
300
|
+
console.error(` Use another port: suveren-gateway config set mcp-port 3440`);
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
275
304
|
ensureDataDir();
|
|
276
305
|
|
|
277
306
|
// NOW save — a start we know is actually going to happen. Also override
|
|
@@ -543,15 +572,26 @@ function printConfigHelp() {
|
|
|
543
572
|
console.log(`suveren-gateway config — read or save gateway settings
|
|
544
573
|
|
|
545
574
|
Usage:
|
|
546
|
-
suveren-gateway config get [as-url|ca-file|pin-tls|proxy]
|
|
575
|
+
suveren-gateway config get [as-url|ca-file|pin-tls|proxy|port|mcp-port|data-dir]
|
|
576
|
+
Print the resolved value(s)
|
|
547
577
|
suveren-gateway config set as-url <url> Save the Authority Server URL
|
|
548
578
|
suveren-gateway config set ca-file <path> Save a CA bundle for internal TLS
|
|
549
579
|
suveren-gateway config set pin-tls on --expect-fingerprint <sha256-hex>
|
|
550
580
|
Pin the Authority Server's TLS certificate
|
|
551
581
|
suveren-gateway config set pin-tls off Stop enforcing the TLS certificate pin
|
|
552
582
|
suveren-gateway config set proxy <url> Save an HTTP(S) proxy (http(s)://host:port)
|
|
553
|
-
|
|
554
|
-
|
|
583
|
+
suveren-gateway config set port <port> Port for the gateway app in the browser (default 3400)
|
|
584
|
+
suveren-gateway config set mcp-port <port> Port AI assistants connect to (default 3430)
|
|
585
|
+
suveren-gateway config set data-dir <folder> Folder for the vault, mandates and logs (default ~/.suveren)
|
|
586
|
+
suveren-gateway config unset port|mcp-port|data-dir Back to the default
|
|
587
|
+
|
|
588
|
+
Saved in ${join(DATA_DIR, 'config.json')} — except port, mcp-port and
|
|
589
|
+
data-dir, which live outside the data folder, in ${installSettingsLocation()}.
|
|
590
|
+
They are kept across restarts and autostart. Ports must be 1024–65535 and
|
|
591
|
+
differ from each other; the data folder must be a full path. Changing the
|
|
592
|
+
data folder does NOT move existing data. Precedence: IT policy > env var
|
|
593
|
+
(SUVEREN_CP_PORT / SUVEREN_MCP_PORT / SUVEREN_DATA_DIR) > saved > default.
|
|
594
|
+
Stop a gateway started with --detach before changing them.
|
|
555
595
|
Precedence at start: IT policy > --as-url flag > env SUVEREN_AS_URL > saved
|
|
556
596
|
as-url > default (${DEFAULT_AS_URL}).
|
|
557
597
|
|
|
@@ -631,6 +671,106 @@ function refuseIfLockedByPolicy(key, humanName, display) {
|
|
|
631
671
|
process.exit(1);
|
|
632
672
|
}
|
|
633
673
|
|
|
674
|
+
// ─── config: ports + data folder (lib/install-settings.mjs) ─────────────
|
|
675
|
+
|
|
676
|
+
/** `config` key → resolved-settings field, policy key and env var. */
|
|
677
|
+
const INSTALL_KEYS = {
|
|
678
|
+
port: { field: 'cpPort', policyKey: 'port', env: 'SUVEREN_CP_PORT', name: 'The gateway port' },
|
|
679
|
+
'mcp-port': { field: 'mcpPort', policyKey: 'mcpPort', env: 'SUVEREN_MCP_PORT', name: 'The MCP port' },
|
|
680
|
+
'data-dir': { field: 'dataDir', policyKey: 'dataDir', env: 'SUVEREN_DATA_DIR', name: 'The data folder' },
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* `config set port|mcp-port|data-dir <value>` and `config unset <key>`
|
|
685
|
+
* (`value` null). Saved outside the data folder — see install-settings.mjs.
|
|
686
|
+
*/
|
|
687
|
+
async function setInstallSetting(key, value) {
|
|
688
|
+
const { field, policyKey, env, name } = INSTALL_KEYS[key];
|
|
689
|
+
refuseIfLockedByPolicy(policyKey, key);
|
|
690
|
+
const clearing = value === null;
|
|
691
|
+
|
|
692
|
+
let next;
|
|
693
|
+
if (!clearing) {
|
|
694
|
+
if (!value) {
|
|
695
|
+
console.error(`Usage: suveren-gateway config set ${key} <${key === 'data-dir' ? 'folder' : 'port'}>`);
|
|
696
|
+
process.exit(2);
|
|
697
|
+
}
|
|
698
|
+
const v = field === 'dataDir' ? validateDataDir(value) : validatePort(value);
|
|
699
|
+
if (!v.ok) {
|
|
700
|
+
console.error(`Invalid ${key}: ${v.error}`);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
703
|
+
next = field === 'dataDir' ? v.path : v.port;
|
|
704
|
+
const other = field === 'cpPort' ? 'mcpPort' : field === 'mcpPort' ? 'cpPort' : null;
|
|
705
|
+
if (other && INSTALL[other] === next) {
|
|
706
|
+
console.error(`Invalid ${key}: ${next} is already the ${other === 'cpPort' ? 'gateway port' : 'MCP port'} — the two must differ.`);
|
|
707
|
+
process.exit(1);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// A gateway this CLI started with --detach keeps its PID file in the
|
|
712
|
+
// CURRENT data folder and listens on the CURRENT ports. Changing either
|
|
713
|
+
// underneath it leaves `stop`/`restart` unable to find it, and a second
|
|
714
|
+
// gateway fighting it for the port. Stop first. (A login-service gateway
|
|
715
|
+
// is fine: `restart` finds that one through the service manager.)
|
|
716
|
+
if (await isAlreadyRunning()) {
|
|
717
|
+
console.error(`The gateway is running (pid ${readPid()}). Stop it first, then change ${key}:`);
|
|
718
|
+
console.error(' suveren-gateway stop');
|
|
719
|
+
process.exit(1);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
try {
|
|
723
|
+
writeInstallSettings({ [field]: clearing ? null : next });
|
|
724
|
+
} catch (err) {
|
|
725
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
726
|
+
process.exit(1);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
const shown = clearing ? `back to the default` : `${next}`;
|
|
730
|
+
console.log(`Saved ${key}: ${shown} (in ${installSettingsLocation()})`);
|
|
731
|
+
|
|
732
|
+
if (process.env[env]) {
|
|
733
|
+
console.log(``);
|
|
734
|
+
console.log(` Note: ${env} is set in this shell (${process.env[env]}) and wins over the saved value.`);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (field === 'mcpPort') {
|
|
738
|
+
const port = clearing ? 3430 : next;
|
|
739
|
+
console.log(``);
|
|
740
|
+
console.log(` AI assistants connect to the new address from the next start:`);
|
|
741
|
+
console.log(` http://localhost:${port}/mcp (SSE: http://localhost:${port}/sse)`);
|
|
742
|
+
console.log(` Update the MCP settings of every assistant you connected before.`);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (field === 'dataDir') {
|
|
746
|
+
const newDir = clearing ? join(homedir(), '.suveren') : next;
|
|
747
|
+
const oldDir = INSTALL.dataDir;
|
|
748
|
+
let oldHasData = false;
|
|
749
|
+
try { oldHasData = resolve(newDir) !== resolve(oldDir) && readdirSync(oldDir).length > 0; } catch { /* no old folder */ }
|
|
750
|
+
if (oldHasData) {
|
|
751
|
+
console.log(``);
|
|
752
|
+
console.log(` Your existing data stays in ${oldDir} — nothing is moved or deleted.`);
|
|
753
|
+
console.log(` The gateway starts EMPTY in ${newDir}: you sign in and set up again.`);
|
|
754
|
+
console.log(` To keep your data instead, copy the folder there yourself before the next start.`);
|
|
755
|
+
}
|
|
756
|
+
// An autostart registered from a shell that had SUVEREN_DATA_DIR set
|
|
757
|
+
// carries it in the service definition, where it wins over this setting.
|
|
758
|
+
const unit = platform() === 'darwin' ? launchAgentPath() : platform() === 'linux' ? systemdUnitPath() : null;
|
|
759
|
+
try {
|
|
760
|
+
if (unit && existsSync(unit) && readFileSync(unit, 'utf8').includes('SUVEREN_DATA_DIR')) {
|
|
761
|
+
console.log(``);
|
|
762
|
+
console.log(` Your login service still sets SUVEREN_DATA_DIR itself (${unit}).`);
|
|
763
|
+
console.log(` Register it again from a shell without that variable: suveren-gateway service install`);
|
|
764
|
+
}
|
|
765
|
+
} catch { /* unreadable — nothing to warn about */ }
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
console.log(``);
|
|
769
|
+
console.log(serviceRunning()
|
|
770
|
+
? ' Restart to apply it: suveren-gateway restart'
|
|
771
|
+
: ' It applies from the next start.');
|
|
772
|
+
}
|
|
773
|
+
|
|
634
774
|
async function config(args) {
|
|
635
775
|
const sub = args[0];
|
|
636
776
|
|
|
@@ -644,6 +784,14 @@ async function config(args) {
|
|
|
644
784
|
console.log(`ca-file: ${(policyCaFile ?? saved.caFile) ?? '(not set)'}${policySuffix('caFile')}`);
|
|
645
785
|
console.log(`pin-tls: ${resolvePinTls(DATA_DIR) ? 'on' : 'off'}${policySuffix('pinTls')}`);
|
|
646
786
|
console.log(`proxy: ${(policyProxy ?? saved.proxyUrl) ?? '(not set)'}${policySuffix('proxy')}`);
|
|
787
|
+
console.log(`port: ${INSTALL.cpPort} (${describeSource(INSTALL.source.cpPort, 'cpPort')})`);
|
|
788
|
+
console.log(`mcp-port: ${INSTALL.mcpPort} (${describeSource(INSTALL.source.mcpPort, 'mcpPort')})`);
|
|
789
|
+
console.log(`data-dir: ${INSTALL.dataDir} (${describeSource(INSTALL.source.dataDir, 'dataDir')})`);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const install = INSTALL_KEYS[key];
|
|
793
|
+
if (install) {
|
|
794
|
+
console.log(`${INSTALL[install.field]} (${describeSource(INSTALL.source[install.field], install.field)})`);
|
|
647
795
|
return;
|
|
648
796
|
}
|
|
649
797
|
if (key === 'as-url') { console.log(`${resolveAsUrl(DATA_DIR)}${policySuffix('asUrl')}`); return; }
|
|
@@ -655,9 +803,18 @@ async function config(args) {
|
|
|
655
803
|
process.exit(2);
|
|
656
804
|
}
|
|
657
805
|
|
|
658
|
-
if (sub === 'set') {
|
|
806
|
+
if (sub === 'set' || sub === 'unset') {
|
|
659
807
|
const key = args[1];
|
|
660
808
|
const value = args[2];
|
|
809
|
+
if (INSTALL_KEYS[key]) {
|
|
810
|
+
await setInstallSetting(key, sub === 'unset' ? null : value);
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
if (sub === 'unset') {
|
|
814
|
+
console.error(`config unset works for port, mcp-port and data-dir only.\n`);
|
|
815
|
+
printConfigHelp();
|
|
816
|
+
process.exit(2);
|
|
817
|
+
}
|
|
661
818
|
if (key === 'as-url') {
|
|
662
819
|
refuseIfLockedByPolicy('asUrl', 'as-url');
|
|
663
820
|
if (!value) {
|
|
@@ -1386,6 +1543,29 @@ async function verifyReport(args) {
|
|
|
1386
1543
|
process.exitCode = await mod.runVerifyReportCli(args);
|
|
1387
1544
|
}
|
|
1388
1545
|
|
|
1546
|
+
/**
|
|
1547
|
+
* `suveren-gateway open` — open the UI in the default browser, on whatever
|
|
1548
|
+
* port is configured. The Windows Start Menu entry and the installer's
|
|
1549
|
+
* Finish checkbox use this (via launcher.cmd) instead of a hard-coded port.
|
|
1550
|
+
*/
|
|
1551
|
+
function openUi() {
|
|
1552
|
+
const url = `http://localhost:${SUVEREN_PORT}`;
|
|
1553
|
+
const os = platform();
|
|
1554
|
+
let child;
|
|
1555
|
+
if (os === 'win32') {
|
|
1556
|
+
// `start ""` — the empty title is required before a quoted target.
|
|
1557
|
+
// Verbatim, or Node escapes those quotes into something cmd misreads.
|
|
1558
|
+
child = spawn('cmd.exe', ['/d', '/s', '/c', `start "" "${url}"`], {
|
|
1559
|
+
detached: true, stdio: 'ignore', windowsHide: true, windowsVerbatimArguments: true,
|
|
1560
|
+
});
|
|
1561
|
+
} else {
|
|
1562
|
+
child = spawn(os === 'darwin' ? 'open' : 'xdg-open', [url], { detached: true, stdio: 'ignore' });
|
|
1563
|
+
}
|
|
1564
|
+
child.on('error', () => { console.log(`Open ${url} in your browser.`); });
|
|
1565
|
+
child.unref();
|
|
1566
|
+
console.log(`Opening ${url}`);
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1389
1569
|
function printHelp() {
|
|
1390
1570
|
console.log(`suveren-gateway — Suveren gateway (Human Agency Protocol)
|
|
1391
1571
|
|
|
@@ -1399,13 +1579,19 @@ Usage:
|
|
|
1399
1579
|
suveren-gateway stop Stop a detached gateway
|
|
1400
1580
|
suveren-gateway restart Stop, then start --detach
|
|
1401
1581
|
suveren-gateway status Show running state + health (incl. simulation mode)
|
|
1582
|
+
suveren-gateway open Open the gateway in your browser
|
|
1402
1583
|
suveren-gateway logs [--tail] Print or tail ~/.suveren/gateway.log
|
|
1403
1584
|
suveren-gateway service <cmd> Run as a login service that survives reboot
|
|
1404
1585
|
(install | uninstall | status)
|
|
1405
|
-
suveren-gateway config get [as-url|ca-file|pin-tls|proxy]
|
|
1586
|
+
suveren-gateway config get [as-url|ca-file|pin-tls|proxy|port|mcp-port|data-dir]
|
|
1587
|
+
Print the resolved value(s)
|
|
1406
1588
|
suveren-gateway config set as-url <url> Save the Authority Server URL
|
|
1407
1589
|
suveren-gateway config set ca-file <path> Save a CA bundle for internal TLS
|
|
1408
1590
|
suveren-gateway config set proxy <url> Save an HTTP(S) proxy (see below)
|
|
1591
|
+
suveren-gateway config set port <port> Save the gateway port (default 3400)
|
|
1592
|
+
suveren-gateway config set mcp-port <port> Save the port AI assistants connect to (default 3430)
|
|
1593
|
+
suveren-gateway config set data-dir <folder>
|
|
1594
|
+
Save the data folder (default ~/.suveren)
|
|
1409
1595
|
suveren-gateway config set pin-tls on --expect-fingerprint <hex>
|
|
1410
1596
|
Pin the Authority Server's TLS certificate
|
|
1411
1597
|
(see \`suveren-gateway config help\` for the fingerprint check)
|
|
@@ -1419,9 +1605,9 @@ Usage:
|
|
|
1419
1605
|
suveren-gateway help Print this help
|
|
1420
1606
|
|
|
1421
1607
|
Environment:
|
|
1422
|
-
SUVEREN_CP_PORT UI + API port (default 3400)
|
|
1423
|
-
SUVEREN_MCP_PORT MCP server port (default 3430)
|
|
1424
|
-
SUVEREN_DATA_DIR Data directory (default ~/.suveren)
|
|
1608
|
+
SUVEREN_CP_PORT UI + API port (default 3400) — overrides a saved \`config set port\`
|
|
1609
|
+
SUVEREN_MCP_PORT MCP server port (default 3430) — overrides a saved \`config set mcp-port\`
|
|
1610
|
+
SUVEREN_DATA_DIR Data directory (default ~/.suveren) — overrides a saved \`config set data-dir\`
|
|
1425
1611
|
SUVEREN_AS_URL Authority Server URL — overrides the saved as-url
|
|
1426
1612
|
SUVEREN_SIMULATION 1 to force simulation mode for this run — overrides the saved setting
|
|
1427
1613
|
HTTP_PROXY / HTTPS_PROXY / NO_PROXY (upper or lower case)
|
|
@@ -1450,6 +1636,7 @@ async function main() {
|
|
|
1450
1636
|
case 'stop': await stop(); break;
|
|
1451
1637
|
case 'status': await status(); break;
|
|
1452
1638
|
case 'restart': await restart(); break;
|
|
1639
|
+
case 'open': openUi(); break;
|
|
1453
1640
|
case 'logs': await logs(argv.slice(1)); break;
|
|
1454
1641
|
case 'service': await service(argv.slice(1)); break;
|
|
1455
1642
|
case 'config': await config(argv.slice(1)); break;
|
|
@@ -2786,6 +2786,21 @@ function requireAllowedHost(req, res, next) {
|
|
|
2786
2786
|
next();
|
|
2787
2787
|
}
|
|
2788
2788
|
|
|
2789
|
+
// src/lib/mcp-public-port.ts
|
|
2790
|
+
function mcpPublicPort(env = process.env, internalUrl) {
|
|
2791
|
+
for (const raw of [env.SUVEREN_MCP_PUBLIC_PORT, env.SUVEREN_MCP_PORT]) {
|
|
2792
|
+
const n = Number(raw);
|
|
2793
|
+
if (raw && Number.isInteger(n) && n > 0 && n <= 65535) return n;
|
|
2794
|
+
}
|
|
2795
|
+
try {
|
|
2796
|
+
const url = new URL(internalUrl ?? env.SUVEREN_MCP_INTERNAL_URL ?? "");
|
|
2797
|
+
const n = Number(url.port);
|
|
2798
|
+
if (Number.isInteger(n) && n > 0) return n;
|
|
2799
|
+
} catch {
|
|
2800
|
+
}
|
|
2801
|
+
return null;
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2789
2804
|
// src/lib/read-policy-defaults.ts
|
|
2790
2805
|
async function backfillReadPolicyDefaults(data) {
|
|
2791
2806
|
const defaults = /* @__PURE__ */ new Map();
|
|
@@ -5001,6 +5016,9 @@ app.get("/health", async (req, res) => {
|
|
|
5001
5016
|
// purely so Settings can render "set by your IT" instead of controls
|
|
5002
5017
|
// that would silently do nothing.
|
|
5003
5018
|
policyLocked: POLICY_LOCKED,
|
|
5019
|
+
// Where AI assistants connect (the UI's setup snippet) — see
|
|
5020
|
+
// lib/mcp-public-port.ts for why the UI must not guess it.
|
|
5021
|
+
mcpPort: mcpPublicPort(process.env, MCP_BASE),
|
|
5004
5022
|
session: buildSessionHealth(vault),
|
|
5005
5023
|
security: {
|
|
5006
5024
|
note: "Gateway secures tool execution. Agent host isolation is the user's responsibility."
|