@suveren/gateway 0.12.0 → 0.13.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.
Files changed (38) hide show
  1. package/README.md +53 -3
  2. package/bin/suveren-gateway.js +128 -12
  3. package/dist/control-plane/index.mjs +555 -122
  4. package/dist/mcp-server/http.mjs +647 -273
  5. package/dist/ui/assets/{index-BzcgeHlq.js → index-CmLqzgqA.js} +21 -21
  6. package/dist/ui/assets/{index-BFOprnFZ.css → index-y1OFeYnS.css} +1 -1
  7. package/dist/ui/index.html +2 -2
  8. package/lib/autostart-templates.d.mts +6 -0
  9. package/lib/autostart-templates.mjs +21 -4
  10. package/lib/config.d.mts +10 -0
  11. package/lib/config.mjs +79 -18
  12. package/lib/policy.d.mts +34 -0
  13. package/lib/policy.mjs +369 -0
  14. package/node_modules/@hap/core/dist/index.d.mts +49 -1
  15. package/node_modules/@hap/core/dist/index.d.ts +49 -1
  16. package/node_modules/@hap/core/dist/index.js +50 -0
  17. package/node_modules/@hap/core/dist/index.mjs +49 -0
  18. package/node_modules/@hap/core/package.json +1 -1
  19. package/node_modules/@hap/core/src/frame.ts +63 -1
  20. package/node_modules/@hap/core/src/gatekeeper.ts +37 -0
  21. package/node_modules/@hap/core/src/types.ts +29 -1
  22. package/node_modules/hono/dist/cjs/hono-base.js +2 -0
  23. package/node_modules/hono/dist/cjs/middleware/language/language.js +1 -1
  24. package/node_modules/hono/dist/cjs/middleware/mount/index.js +71 -0
  25. package/node_modules/hono/dist/cjs/request.js +4 -4
  26. package/node_modules/hono/dist/hono-base.js +2 -0
  27. package/node_modules/hono/dist/middleware/mount/index.js +70 -0
  28. package/node_modules/hono/dist/request.js +4 -4
  29. package/node_modules/hono/dist/types/hono-base.d.ts +2 -0
  30. package/node_modules/hono/dist/types/jsx/base.d.ts +1 -1
  31. package/node_modules/hono/dist/types/middleware/mount/index.d.ts +46 -0
  32. package/node_modules/hono/dist/types/request.d.ts +4 -4
  33. package/node_modules/hono/package.json +9 -1
  34. package/package.json +2 -2
  35. package/profiles/index.json +3 -1
  36. package/profiles/purchase/0.6.profile.json +234 -0
  37. package/profiles/sales/0.3.profile.json +290 -0
  38. package/server.js +51 -8
package/README.md CHANGED
@@ -89,7 +89,7 @@ No credentials. No signing keys. Just the scope of what the agent is allowed to
89
89
 
90
90
  ## Quick Start
91
91
 
92
- Pick whichever you have on hand — both produce the same gateway.
92
+ Pick whichever fits — all three produce the same gateway. For company laptops managed by IT, use Option C.
93
93
 
94
94
  ### Option A — Docker
95
95
 
@@ -99,7 +99,7 @@ Requires [Docker](https://docs.docker.com/get-docker/).
99
99
  docker run -d --name suveren-gateway \
100
100
  -p 7400:3000 -p 7430:3030 \
101
101
  -v $HOME/.suveren:/app/data \
102
- ghcr.io/humanagencyprotocol/suveren-gateway
102
+ ghcr.io/suverenai/suveren-gateway
103
103
  ```
104
104
 
105
105
  Open `http://localhost:7400`. The MCP server is at `http://localhost:7430`.
@@ -130,9 +130,23 @@ suveren-gateway restart
130
130
  PowerShell 5.1, which is what ships with Windows. Use `;` there if you want
131
131
  them on one line.)
132
132
 
133
+ ### Option C — Windows installer (company laptops)
134
+
135
+ For laptops managed by a company's IT: no admin rights, no Node.js and no access to the public npm registry needed. Every [GitHub Release](https://github.com/suverenai/suveren-gateway/releases) from 0.13.0 on carries:
136
+
137
+ | File | What it is |
138
+ |---|---|
139
+ | `suveren-gateway-<version>-windows-unsigned.msi` | Per-user installer: the gateway, its own Node.js and the tested connector versions — nothing is downloaded at runtime |
140
+ | `suveren-gateway-<version>-windows-signing-kit.zip` | The unpacked contents, the installer source and `build-signed.ps1`, so IT can sign every file with its own certificate and rebuild the `.msi` |
141
+ | `SHA256SUMS` | Checksums of both files |
142
+
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
+
145
+ Open `http://localhost:3400` (Start menu: *Suveren Gateway*). The MCP server is at `http://localhost:3430`.
146
+
133
147
  ### Connecting an MCP client
134
148
 
135
- Either path exposes the same MCP transports — use the port from the path you chose (7430 for Docker, 3430 for npm):
149
+ Every option exposes the same MCP transports — use the port from the option you chose (7430 for Docker, 3430 for npm and the Windows installer):
136
150
 
137
151
  ```
138
152
  Streamable HTTP: POST http://localhost:<port>/mcp
@@ -185,6 +199,40 @@ See `suveren-gateway config help` for the full command reference.
185
199
 
186
200
  ---
187
201
 
202
+ ## Behind a company proxy
203
+
204
+ On a company laptop that routes all internet traffic through an HTTP(S) proxy, the gateway needs two things: the proxy address, and — if that proxy does TLS inspection — the company's root certificate.
205
+
206
+ **The proxy.** Set the standard environment variables (upper or lower case both work) before starting the gateway, or save one with the CLI:
207
+
208
+ ```bash
209
+ export HTTPS_PROXY=http://proxy.corp.example:8080
210
+ export HTTP_PROXY=http://proxy.corp.example:8080 # usually the same value
211
+ export NO_PROXY=internal.example.com # optional: hosts to reach directly
212
+ suveren-gateway start
213
+
214
+ # or, to save it so every future start picks it up without re-exporting:
215
+ suveren-gateway config set proxy http://proxy.corp.example:8080
216
+ suveren-gateway restart
217
+ ```
218
+
219
+ Every outbound call this applies to — the Authority Server, the update checker, and a remote AI assistant endpoint — honours the proxy. A loopback target (the control plane and MCP server talking to each other, or a local AI assistant such as Ollama) is **never** proxied, no matter what is set — a corporate proxy has no route back to the machine it's running on.
220
+
221
+ **TLS inspection.** Many corporate proxies intercept HTTPS by re-signing every certificate with their own root — that includes the Authority Server's. If you see a certificate error mentioning an untrusted issuer, trust the company's root the same way you would for a self-hosted Authority Server:
222
+
223
+ ```bash
224
+ suveren-gateway config set ca-file /path/to/company-root-ca.pem
225
+ suveren-gateway restart
226
+ ```
227
+
228
+ This works through the proxy exactly like it works directly — `--ca-file` applies to the certificate the gateway actually sees, proxied or not.
229
+
230
+ **The one combination that still refuses, on purpose:** [TLS pinning](#pinning-the-authority-servers-tls-certificate) (opt-in, off by default) together with a TLS-inspecting proxy. Pinning checks the certificate the gateway sees *through* the proxy — if that's the proxy's own re-signed one rather than the real Authority Server's, the pin will not match, and the gateway refuses the connection and says so, even with the company root trusted via `--ca-file`. That refusal is correct: ask network/IT to exempt the Authority Server's host from TLS inspection, the same request you'd make for any pinned certificate on that network.
231
+
232
+ **On a company-managed machine, IT can set the proxy centrally** instead of relying on `HTTPS_PROXY` being exported in every shell — see [Managed settings](docs/managed-settings.md). A policy-set `Proxy` (Windows registry or a JSON policy file) **overrides** an already-set `HTTP_PROXY`/`HTTPS_PROXY` rather than merely filling it in, and `config set proxy` / `--proxy` refuse with "set by your IT" when it's locked. A loopback target is still never proxied, regardless.
233
+
234
+ ---
235
+
188
236
  ## Simulation mode — block every real system
189
237
 
190
238
  A mandate is bound to a *profile* (e.g. "sales"), not to a specific connector. If a real
@@ -229,6 +277,8 @@ for the full command reference.
229
277
  | [Authorization Flow](docs/authorization-flow.md) | Data flow, gate wizard, tool execution, agent context |
230
278
  | [Security Model](docs/security.md) | Enforcement layers, verification, encryption, fail-closed design |
231
279
  | [Development](docs/development.md) | Local setup, env vars, Docker, testing |
280
+ | [Windows IT guide](docs/windows-it-guide.md) | Verify, sign and distribute the Windows installer; network and updates |
281
+ | [Managed settings](docs/managed-settings.md) | Settings IT presets and locks (registry or policy file) |
232
282
 
233
283
  ---
234
284
 
@@ -17,10 +17,11 @@ import { homedir, platform, userInfo } from 'node:os';
17
17
  import { dirname, join, resolve } from 'node:path';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { buildLaunchAgentPlist, buildMacLauncher, buildSystemdUnit, buildWindowsTaskXml } from '../lib/autostart-templates.mjs';
20
- import { DEFAULT_AS_URL, readConfig, writeConfig, validateAsUrl, validateCaFile, validatePinTls, resolveAsUrl, resolvePinTls, resolvePinTlsExpectedFingerprint, resolveSimulation } from '../lib/config.mjs';
20
+ import { DEFAULT_AS_URL, readConfig, writeConfig, validateAsUrl, validateCaFile, validateProxyUrl, validatePinTls, resolveAsUrl, resolvePinTls, resolvePinTlsExpectedFingerprint, resolveSimulation } from '../lib/config.mjs';
21
21
  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
+ import { readPolicy, isPolicyLocked } from '../lib/policy.mjs';
24
25
 
25
26
  // Before any command: on an unsupported Node the connectors cannot run (see
26
27
  // lib/node-version.mjs), so refuse with the reason instead of starting.
@@ -199,6 +200,18 @@ async function start(args) {
199
200
  caFileToSave = v.path;
200
201
  }
201
202
 
203
+ const proxyFlag = flagValue(args, '--proxy');
204
+ let proxyToSave = null;
205
+ if (proxyFlag) {
206
+ refuseIfLockedByPolicy('proxy', 'proxy');
207
+ const v = validateProxyUrl(proxyFlag);
208
+ if (!v.ok) {
209
+ console.error(`Invalid --proxy: ${v.error}`);
210
+ process.exit(1);
211
+ }
212
+ proxyToSave = v.url;
213
+ }
214
+
202
215
  // --pin-tls: a boolean flag (turns TLS certificate pinning ON — there is
203
216
  // no `start --pin-tls off`; use `config set pin-tls off` for that).
204
217
  // Validated against the EFFECTIVE as-url for this very start (the flag
@@ -275,6 +288,12 @@ async function start(args) {
275
288
  writeConfig(DATA_DIR, { caFile: caFileToSave });
276
289
  console.log(`CA file: ${caFileToSave} (saved — applied to every process this gateway starts)`);
277
290
  }
291
+ if (proxyToSave) {
292
+ writeConfig(DATA_DIR, { proxyUrl: proxyToSave });
293
+ process.env.HTTP_PROXY ??= proxyToSave;
294
+ process.env.HTTPS_PROXY ??= proxyToSave;
295
+ console.log(`Proxy: ${proxyToSave} (saved — applied to every process this gateway starts, unless HTTP_PROXY/HTTPS_PROXY is already set)`);
296
+ }
278
297
  if (pinTlsToSave) {
279
298
  const effectiveAsUrl = asUrlToSave ?? resolveAsUrl(DATA_DIR);
280
299
  const committed = commitConfirmedFingerprint(DATA_DIR, effectiveAsUrl, confirmedFingerprintHex);
@@ -519,16 +538,36 @@ function printConfigHelp() {
519
538
  console.log(`suveren-gateway config — read or save gateway settings
520
539
 
521
540
  Usage:
522
- suveren-gateway config get [as-url|ca-file|pin-tls] Print the resolved value(s)
541
+ suveren-gateway config get [as-url|ca-file|pin-tls|proxy] Print the resolved value(s)
523
542
  suveren-gateway config set as-url <url> Save the Authority Server URL
524
543
  suveren-gateway config set ca-file <path> Save a CA bundle for internal TLS
525
544
  suveren-gateway config set pin-tls on --expect-fingerprint <sha256-hex>
526
545
  Pin the Authority Server's TLS certificate
527
546
  suveren-gateway config set pin-tls off Stop enforcing the TLS certificate pin
547
+ suveren-gateway config set proxy <url> Save an HTTP(S) proxy (http(s)://host:port)
528
548
 
529
549
  Saved in ${join(DATA_DIR, 'config.json')}.
530
- Precedence at start: --as-url flag > env SUVEREN_AS_URL > saved as-url >
531
- default (${DEFAULT_AS_URL}).
550
+ Precedence at start: IT policy > --as-url flag > env SUVEREN_AS_URL > saved
551
+ as-url > default (${DEFAULT_AS_URL}).
552
+
553
+ A setting your organization's IT has set centrally (Windows registry policy,
554
+ or a policy file — see docs/managed-settings.md) is LOCKED: \`config get\`
555
+ shows "(set by your IT)" next to it, and \`config set\` refuses to change it.
556
+
557
+ proxy is a convenience for operators without shell access to set
558
+ HTTP_PROXY/HTTPS_PROXY themselves — those environment variables remain the
559
+ actual requirement (every process this gateway runs honours them directly)
560
+ and always win if already set; a saved proxy only fills in whichever of the
561
+ two is still unset. NO_PROXY is honoured too, read directly from the
562
+ environment (there is no saved equivalent). A loopback target (the control
563
+ plane and the MCP server talking to each other, or a local AI assistant) is
564
+ never proxied, independent of any of this.
565
+
566
+ An IT-set Proxy policy is the ONE locked setting that OVERRIDES an
567
+ already-set HTTP_PROXY/HTTPS_PROXY rather than merely out-ranking a saved
568
+ value — the Windows-company-laptop case, where the proxy lives in system
569
+ settings, not something an employee's shell exports. A NoProxy policy
570
+ likewise overrides NO_PROXY. See docs/managed-settings.md.
532
571
 
533
572
  pin-tls (default off, self-hosted Authority Servers with a stable signing
534
573
  key) requires https:// and pins the Authority Server's TLS certificate
@@ -564,21 +603,48 @@ running gateway keeps using what it already resolved at its own startup.
564
603
  `);
565
604
  }
566
605
 
606
+ /** Appended to `config get` output when IT policy has set this key — see
607
+ * lib/policy.mjs. Purely informational here; the refusal itself lives in
608
+ * `refuseIfLockedByPolicy`, called from `config set` / `simulation`. */
609
+ function policySuffix(key) {
610
+ return isPolicyLocked(key) ? ' (set by your IT)' : '';
611
+ }
612
+
613
+ /**
614
+ * Refuse a `config set` / `simulation on|off` for a key IT policy already
615
+ * controls. A policy value is the top precedence tier (see policy.mjs) —
616
+ * writing a saved config.json value anyway would be actively misleading
617
+ * (`config get` would show something that is NOT what's actually running),
618
+ * so this exits before any validation or write happens, naming the policy
619
+ * value so the person doesn't have to go find it themselves.
620
+ */
621
+ function refuseIfLockedByPolicy(key, humanName, display) {
622
+ if (!isPolicyLocked(key)) return;
623
+ const value = readPolicy().policy[key];
624
+ console.error(`${humanName} is set by your IT policy (${display ? display(value) : value}) and cannot be changed here.`);
625
+ console.error('Contact your IT administrator to change it.');
626
+ process.exit(1);
627
+ }
628
+
567
629
  async function config(args) {
568
630
  const sub = args[0];
569
631
 
570
632
  if (sub === 'get') {
571
633
  const key = args[1];
572
634
  const saved = readConfig(DATA_DIR);
635
+ const policyCaFile = readPolicy().policy.caFile;
636
+ const policyProxy = readPolicy().policy.proxy;
573
637
  if (!key) {
574
- console.log(`as-url: ${resolveAsUrl(DATA_DIR)}`);
575
- console.log(`ca-file: ${saved.caFile ?? '(not set)'}`);
576
- console.log(`pin-tls: ${resolvePinTls(DATA_DIR) ? 'on' : 'off'}`);
638
+ console.log(`as-url: ${resolveAsUrl(DATA_DIR)}${policySuffix('asUrl')}`);
639
+ console.log(`ca-file: ${(policyCaFile ?? saved.caFile) ?? '(not set)'}${policySuffix('caFile')}`);
640
+ console.log(`pin-tls: ${resolvePinTls(DATA_DIR) ? 'on' : 'off'}${policySuffix('pinTls')}`);
641
+ console.log(`proxy: ${(policyProxy ?? saved.proxyUrl) ?? '(not set)'}${policySuffix('proxy')}`);
577
642
  return;
578
643
  }
579
- if (key === 'as-url') { console.log(resolveAsUrl(DATA_DIR)); return; }
580
- if (key === 'ca-file') { console.log(saved.caFile ?? ''); return; }
581
- if (key === 'pin-tls') { console.log(resolvePinTls(DATA_DIR) ? 'on' : 'off'); return; }
644
+ if (key === 'as-url') { console.log(`${resolveAsUrl(DATA_DIR)}${policySuffix('asUrl')}`); return; }
645
+ if (key === 'ca-file') { console.log(`${(policyCaFile ?? saved.caFile) ?? ''}${policySuffix('caFile')}`); return; }
646
+ if (key === 'pin-tls') { console.log(`${resolvePinTls(DATA_DIR) ? 'on' : 'off'}${policySuffix('pinTls')}`); return; }
647
+ if (key === 'proxy') { console.log(`${(policyProxy ?? saved.proxyUrl) ?? ''}${policySuffix('proxy')}`); return; }
582
648
  console.error(`Unknown config key: ${key}\n`);
583
649
  printConfigHelp();
584
650
  process.exit(2);
@@ -588,6 +654,7 @@ async function config(args) {
588
654
  const key = args[1];
589
655
  const value = args[2];
590
656
  if (key === 'as-url') {
657
+ refuseIfLockedByPolicy('asUrl', 'as-url');
591
658
  if (!value) {
592
659
  console.error('Usage: suveren-gateway config set as-url <url>');
593
660
  process.exit(2);
@@ -603,6 +670,7 @@ async function config(args) {
603
670
  return;
604
671
  }
605
672
  if (key === 'ca-file') {
673
+ refuseIfLockedByPolicy('caFile', 'ca-file');
606
674
  if (!value) {
607
675
  console.error('Usage: suveren-gateway config set ca-file <path>');
608
676
  process.exit(2);
@@ -617,7 +685,25 @@ async function config(args) {
617
685
  console.log('Restart to pick it up: suveren-gateway restart');
618
686
  return;
619
687
  }
688
+ if (key === 'proxy') {
689
+ refuseIfLockedByPolicy('proxy', 'proxy');
690
+ if (!value) {
691
+ console.error('Usage: suveren-gateway config set proxy <url>');
692
+ process.exit(2);
693
+ }
694
+ const v = validateProxyUrl(value);
695
+ if (!v.ok) {
696
+ console.error(`Invalid proxy: ${v.error}`);
697
+ process.exit(1);
698
+ }
699
+ writeConfig(DATA_DIR, { proxyUrl: v.url });
700
+ console.log(`Saved proxy: ${v.url}`);
701
+ console.log('Sets HTTP_PROXY/HTTPS_PROXY for every process this gateway starts, unless already set in the environment.');
702
+ console.log('Restart to pick it up: suveren-gateway restart');
703
+ return;
704
+ }
620
705
  if (key === 'pin-tls') {
706
+ refuseIfLockedByPolicy('pinTls', 'pin-tls', (v) => (v ? 'on' : 'off'));
621
707
  if (value !== 'on' && value !== 'off') {
622
708
  console.error('Usage: suveren-gateway config set pin-tls on|off [--expect-fingerprint <sha256-hex>]');
623
709
  process.exit(2);
@@ -679,12 +765,17 @@ Usage:
679
765
  suveren-gateway simulation status Show the saved setting and (if running) the live one
680
766
 
681
767
  Changes take effect on the next \`suveren-gateway start\` / \`restart\`.
768
+
769
+ If your organization's IT has set Simulation centrally (Windows registry
770
+ policy, or a policy file — see docs/managed-settings.md), it is LOCKED: both
771
+ \`on\` and \`off\` refuse, naming the policy value, and this cannot be
772
+ overridden locally — not even by setting SUVEREN_SIMULATION directly.
682
773
  `);
683
774
  }
684
775
 
685
776
  async function printSimulationStatus() {
686
777
  const saved = resolveSimulation(DATA_DIR);
687
- console.log(`Saved: ${saved ? 'on' : 'off'}`);
778
+ console.log(`Saved: ${saved ? 'on' : 'off'}${policySuffix('simulation')}`);
688
779
  try {
689
780
  const res = await fetch(`http://localhost:${SUVEREN_PORT}/health`, { signal: AbortSignal.timeout(3000) });
690
781
  if (res.ok) {
@@ -720,6 +811,7 @@ async function simulation(args) {
720
811
  }
721
812
 
722
813
  if (sub === 'on') {
814
+ refuseIfLockedByPolicy('simulation', 'Simulation mode', (v) => (v ? 'on' : 'off'));
723
815
  // The safe direction — blocking real systems needs no confirmation.
724
816
  writeConfig(DATA_DIR, { simulation: true });
725
817
  console.log('Simulation mode: ON (saved) — every connector without a manifest "simulation" marker will be blocked.');
@@ -728,6 +820,7 @@ async function simulation(args) {
728
820
  }
729
821
 
730
822
  if (sub === 'off') {
823
+ refuseIfLockedByPolicy('simulation', 'Simulation mode', (v) => (v ? 'on' : 'off'));
731
824
  console.error('Turning simulation mode OFF makes every real system reachable again: any connector');
732
825
  console.error('without a manifest "simulation" marker (e.g. a live Gmail or ERP account) will spawn');
733
826
  console.error('and execute for real the next time this gateway starts.');
@@ -995,6 +1088,19 @@ async function serviceInstallWindows() {
995
1088
  // Deliberately does NOT stop the running gateway — see the macOS note.
996
1089
  // Registering is enough; Task Scheduler starts it at the next logon.
997
1090
 
1091
+ // SUVEREN_LAUNCHER (set by the Windows installer's launcher.cmd, never by
1092
+ // a plain npm/dev install) means this `service install` was itself invoked
1093
+ // THROUGH that launcher — route the scheduled task through it too, instead
1094
+ // of straight at node.exe/server.js. The launcher sets SUVEREN_INSTALL_
1095
+ // METHOD/SUVEREN_OFFLINE/SUVEREN_INTEGRATIONS_DIR/PATH itself, scoped to
1096
+ // just the gateway process it starts; writing those into HKCU\Environment
1097
+ // (an earlier version of the installer did this) is ACCOUNT-WIDE — it
1098
+ // would shadow a developer's own Node for every other program on that
1099
+ // account and force every other gateway install under the same user into
1100
+ // offline+managed mode too. Absent (every non-managed install), this is a
1101
+ // no-op: buildWindowsTaskXml renders identically to before.
1102
+ const launcherPath = process.env.SUVEREN_LAUNCHER;
1103
+
998
1104
  // schtasks reads the XML from a file and requires UTF-16 LE with a BOM —
999
1105
  // it rejects UTF-8 with an unhelpful "The task XML is malformed".
1000
1106
  const xml = buildWindowsTaskXml({
@@ -1003,6 +1109,7 @@ async function serviceInstallWindows() {
1003
1109
  author: 'Suveren',
1004
1110
  dataDir: process.env.SUVEREN_DATA_DIR ?? '',
1005
1111
  userId: windowsUserId(),
1112
+ ...(launcherPath ? { command: launcherPath, args: 'run' } : {}),
1006
1113
  });
1007
1114
  const xmlPath = join(DATA_DIR, 'suveren-task.xml');
1008
1115
  writeFileSync(xmlPath, '\ufeff' + xml, { encoding: 'utf16le' });
@@ -1257,6 +1364,7 @@ Usage:
1257
1364
  suveren-gateway start [--detach] Run the gateway (foreground by default)
1258
1365
  [--as-url <url>] Authority Server URL (saved for next time)
1259
1366
  [--ca-file <path>] Internal CA bundle (saved for next time)
1367
+ [--proxy <url>] HTTP(S) proxy (saved for next time — see below)
1260
1368
  [--pin-tls --expect-fingerprint <hex>] Pin the AS TLS certificate (saved; https:// only — see below)
1261
1369
  [--simulation] Block every real system (saved — see \`simulation help\`)
1262
1370
  suveren-gateway stop Stop a detached gateway
@@ -1265,9 +1373,10 @@ Usage:
1265
1373
  suveren-gateway logs [--tail] Print or tail ~/.suveren/gateway.log
1266
1374
  suveren-gateway service <cmd> Run as a login service that survives reboot
1267
1375
  (install | uninstall | status)
1268
- suveren-gateway config get [as-url|ca-file|pin-tls] Print the resolved value(s)
1376
+ suveren-gateway config get [as-url|ca-file|pin-tls|proxy] Print the resolved value(s)
1269
1377
  suveren-gateway config set as-url <url> Save the Authority Server URL
1270
1378
  suveren-gateway config set ca-file <path> Save a CA bundle for internal TLS
1379
+ suveren-gateway config set proxy <url> Save an HTTP(S) proxy (see below)
1271
1380
  suveren-gateway config set pin-tls on --expect-fingerprint <hex>
1272
1381
  Pin the Authority Server's TLS certificate
1273
1382
  (see \`suveren-gateway config help\` for the fingerprint check)
@@ -1282,6 +1391,13 @@ Environment:
1282
1391
  SUVEREN_DATA_DIR Data directory (default ~/.suveren)
1283
1392
  SUVEREN_AS_URL Authority Server URL — overrides the saved as-url
1284
1393
  SUVEREN_SIMULATION 1 to force simulation mode for this run — overrides the saved setting
1394
+ HTTP_PROXY / HTTPS_PROXY / NO_PROXY (upper or lower case)
1395
+ Corporate proxy — honoured for every outbound call to the Authority
1396
+ Server, the update checker, and a remote AI assistant endpoint. A
1397
+ loopback target (127.0.0.1/localhost, incl. a local AI assistant) is
1398
+ never proxied. These win over a saved \`config set proxy\` — UNLESS
1399
+ your organization's IT has set a Proxy/NoProxy policy, which wins
1400
+ over both (see docs/managed-settings.md).
1285
1401
 
1286
1402
  Authority Server resolution order: --as-url flag > SUVEREN_AS_URL env >
1287
1403
  saved as-url (\`config set as-url\`) > default (${DEFAULT_AS_URL}).