@solongate/proxy 0.83.9 → 0.83.10

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/dist/index.js CHANGED
@@ -12363,6 +12363,12 @@ function SettingsPanel({
12363
12363
  ] }, "h:" + sec)
12364
12364
  );
12365
12365
  lineKey.push("");
12366
+ if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
12367
+ lineEls.push(
12368
+ /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
12369
+ );
12370
+ lineKey.push("");
12371
+ }
12366
12372
  }
12367
12373
  lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
12368
12374
  lineKey.push(keyOf(r));
package/dist/tui/index.js CHANGED
@@ -5489,6 +5489,12 @@ function SettingsPanel({
5489
5489
  ] }, "h:" + sec)
5490
5490
  );
5491
5491
  lineKey.push("");
5492
+ if ((sec === "WEBHOOKS" || sec === "ALERTS") && local?.enabled) {
5493
+ lineEls.push(
5494
+ /* @__PURE__ */ jsx8(Text8, { wrap: "truncate", color: theme.warn, children: ` \u26A0 local logs on \u2014 real denials stay on this machine, so ${sec.toLowerCase()} do NOT fire${sec === "WEBHOOKS" ? " (t test still works)" : ""}` }, "warn:" + sec)
5495
+ );
5496
+ lineKey.push("");
5497
+ }
5492
5498
  }
5493
5499
  lineEls.push(/* @__PURE__ */ jsx8(Box8, { children: rowLine(r) }, keyOf(r)));
5494
5500
  lineKey.push(keyOf(r));
package/hooks/audit.mjs CHANGED
@@ -11,7 +11,7 @@ import { homedir } from 'node:os';
11
11
  // Bump on every audit hook change. The cloud serves the newest version; the guard
12
12
  // hook installs it on its next run (no re-login needed). See guard.mjs
13
13
  // fetchAndInstallHook / maybeSelfUpdate.
14
- const HOOK_VERSION = 25;
14
+ const HOOK_VERSION = 26;
15
15
 
16
16
  function loadEnvKey(dir) {
17
17
  try {
@@ -748,11 +748,8 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
748
748
  try { rateLimitBurst = rateLimitObserveBurst(AGENT_ID, loadRateLimitObserve()); } catch { rateLimitBurst = false; }
749
749
  }
750
750
 
751
- // Local log storage (opt-in) MIRRORS: the entry is appended to the user's
752
- // chosen file AND still sent to the cloud. It used to divert instead, so a
753
- // project with local logs on saw an empty dashboard with nothing to say the
754
- // emptiness was deliberate — while the dataroom called the same setting
755
- // "mirror every decision to a file".
751
+ // Local log storage (opt-in): when ON, logs are kept LOCAL ONLY — we append
752
+ // this entry to the user's chosen file and do NOT send it to the cloud.
756
753
  const localLogs = loadLocalLogs();
757
754
  if (localLogs) {
758
755
  appendLocalLog(localLogs, {
@@ -762,8 +759,9 @@ try { input += readFileSync(0, 'utf-8'); } catch {}
762
759
  ...(dlpMatches.length ? { dlp: dlpMatches } : {}),
763
760
  ...(rateLimitBurst ? { rate_limit_burst: true } : {}),
764
761
  });
765
- }
766
- {
762
+ fetchDone = true;
763
+ maybeExit();
764
+ } else {
767
765
  // Fire-and-forget: don't block tool execution waiting for API response
768
766
  fetch(`${API_URL}/api/v1/audit-logs`, {
769
767
  method: 'POST',
@@ -6536,7 +6536,7 @@ import { resolve, join, dirname, isAbsolute } from "node:path";
6536
6536
  import { homedir } from "node:os";
6537
6537
  import { gunzipSync } from "node:zlib";
6538
6538
  import { createHash } from "node:crypto";
6539
- var HOOK_VERSION = 74;
6539
+ var HOOK_VERSION = 75;
6540
6540
  function localLogsOnly(security) {
6541
6541
  if (security !== void 0) {
6542
6542
  const l = security && security.localLogs;
@@ -8428,12 +8428,13 @@ if (!REFRESH_MODE) {
8428
8428
  } catch {
8429
8429
  }
8430
8430
  try {
8431
- await fetch(API_URL + "/api/v1/audit-logs", {
8432
- method: "POST",
8433
- headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8434
- body: JSON.stringify(_logEntry),
8435
- signal: AbortSignal.timeout(3e3)
8436
- });
8431
+ if (!localLogsOnly(_sec))
8432
+ await fetch(API_URL + "/api/v1/audit-logs", {
8433
+ method: "POST",
8434
+ headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8435
+ body: JSON.stringify(_logEntry),
8436
+ signal: AbortSignal.timeout(3e3)
8437
+ });
8437
8438
  } catch {
8438
8439
  }
8439
8440
  if (AGENT_TYPE !== "codex")
@@ -8545,23 +8546,24 @@ if (!REFRESH_MODE) {
8545
8546
  }
8546
8547
  writeLocalLog(securityCfg, { ts: (/* @__PURE__ */ new Date()).toISOString(), tool: toolName, arguments: args, decision: "DENY", reason: "ghost path (hidden from agent)", permission: guessPermission(toolName), source: `${AGENT_TYPE}-guard`, agent_id: AGENT_TYPE, agent_name: AGENT_NAME, session_id: call.sessionId, evaluation_time_ms: Date.now() - _evalStart });
8547
8548
  try {
8548
- await fetch(API_URL + "/api/v1/audit-logs", {
8549
- method: "POST",
8550
- headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8551
- body: JSON.stringify({
8552
- tool: toolName,
8553
- arguments: args,
8554
- decision: "DENY",
8555
- reason: "ghost path (hidden from agent)",
8556
- permission: guessPermission(toolName),
8557
- source: `${AGENT_TYPE}-guard`,
8558
- agent_id: AGENT_TYPE,
8559
- agent_name: AGENT_NAME,
8560
- session_id: call.sessionId,
8561
- evaluation_time_ms: Date.now() - _evalStart
8562
- }),
8563
- signal: AbortSignal.timeout(3e3)
8564
- });
8549
+ if (!localLogsOnly(securityCfg))
8550
+ await fetch(API_URL + "/api/v1/audit-logs", {
8551
+ method: "POST",
8552
+ headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8553
+ body: JSON.stringify({
8554
+ tool: toolName,
8555
+ arguments: args,
8556
+ decision: "DENY",
8557
+ reason: "ghost path (hidden from agent)",
8558
+ permission: guessPermission(toolName),
8559
+ source: `${AGENT_TYPE}-guard`,
8560
+ agent_id: AGENT_TYPE,
8561
+ agent_name: AGENT_NAME,
8562
+ session_id: call.sessionId,
8563
+ evaluation_time_ms: Date.now() - _evalStart
8564
+ }),
8565
+ signal: AbortSignal.timeout(3e3)
8566
+ });
8565
8567
  } catch {
8566
8568
  }
8567
8569
  blockTool(ghostHit, true);
@@ -8642,12 +8644,13 @@ if (!REFRESH_MODE) {
8642
8644
  evaluation_time_ms: Date.now() - _evalStart
8643
8645
  };
8644
8646
  writeLocalLog(securityCfg, { ts: (/* @__PURE__ */ new Date()).toISOString(), ...logEntry });
8645
- await fetch(API_URL + "/api/v1/audit-logs", {
8646
- method: "POST",
8647
- headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8648
- body: JSON.stringify(logEntry),
8649
- signal: AbortSignal.timeout(3e3)
8650
- });
8647
+ if (!localLogsOnly(securityCfg))
8648
+ await fetch(API_URL + "/api/v1/audit-logs", {
8649
+ method: "POST",
8650
+ headers: { "Content-Type": "application/json", ...AUTH_HEADERS },
8651
+ body: JSON.stringify(logEntry),
8652
+ signal: AbortSignal.timeout(3e3)
8653
+ });
8651
8654
  } catch {
8652
8655
  }
8653
8656
  }
package/hooks/guard.mjs CHANGED
@@ -32,13 +32,11 @@ import { createHash } from 'node:crypto';
32
32
  // the installed hook self-updates when the cloud version is higher (see
33
33
  // maybeSelfUpdate). This is what makes guard fixes propagate without a manual
34
34
  // reinstall — the same trust model as the OPA WASM this hook already runs.
35
- const HOOK_VERSION = 74;
35
+ const HOOK_VERSION = 75;
36
36
 
37
- // True when local log storage is ON meaning a copy is written to this
38
- // machine. It is a MIRROR, not a diversion: the cloud audit log is written
39
- // either way. The dataroom always described it as "mirror every decision to a
40
- // file"; the code diverted instead, so a project with local logs on had nothing
41
- // in the dashboard and no way to tell that was deliberate.
37
+ // True when local log storage is ON. In that mode logs are kept LOCAL ONLY and
38
+ // nothing is sent to the cloud audit log local and cloud are one or the
39
+ // other, never both.
42
40
  //
43
41
  // `undefined` is the ONLY value that means "we do not know yet". `null` is an
44
42
  // answer: it is what the refresh writes when the API returns no security block,
@@ -2426,7 +2424,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
2426
2424
  };
2427
2425
  try { writeLocalLog(_sec, { ts: new Date().toISOString(), ..._logEntry }); } catch {}
2428
2426
  try {
2429
- await fetch(API_URL + '/api/v1/audit-logs', {
2427
+ if (!localLogsOnly(_sec)) await fetch(API_URL + '/api/v1/audit-logs', {
2430
2428
  method: 'POST',
2431
2429
  headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
2432
2430
  body: JSON.stringify(_logEntry),
@@ -2579,7 +2577,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
2579
2577
  try { writeDenyFlag(toolName); } catch {}
2580
2578
  writeLocalLog(securityCfg, { ts: new Date().toISOString(), tool: toolName, arguments: args, decision: 'DENY', reason: 'ghost path (hidden from agent)', permission: guessPermission(toolName), source: `${AGENT_TYPE}-guard`, agent_id: AGENT_TYPE, agent_name: AGENT_NAME, session_id: call.sessionId, evaluation_time_ms: Date.now() - _evalStart });
2581
2579
  try {
2582
- await fetch(API_URL + '/api/v1/audit-logs', {
2580
+ if (!localLogsOnly(securityCfg)) await fetch(API_URL + '/api/v1/audit-logs', {
2583
2581
  method: 'POST',
2584
2582
  headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
2585
2583
  body: JSON.stringify({
@@ -2721,7 +2719,7 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
2721
2719
  writeLocalLog(securityCfg, { ts: new Date().toISOString(), ...logEntry });
2722
2720
  // PI hook layer removed — piResult fields no longer attached.
2723
2721
  // Local-only mode: keep the log on the user's machine, skip the cloud.
2724
- await fetch(API_URL + '/api/v1/audit-logs', {
2722
+ if (!localLogsOnly(securityCfg)) await fetch(API_URL + '/api/v1/audit-logs', {
2725
2723
  method: 'POST',
2726
2724
  headers: { 'Content-Type': 'application/json', ...AUTH_HEADERS },
2727
2725
  body: JSON.stringify(logEntry),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.83.9",
3
+ "version": "0.83.10",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {