@fidacy/openclaw-plugin 0.5.5 → 0.5.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Four configuration mistakes that presented themselves as security verdicts, so the firewall looked broken and stopped real payments for reasons nobody could see.
8
+
9
+ - `FIDACY_MANDATE_JSON` was the last human-authored surface returned verbatim, with no validation of its caps. `"perTxMax": "10000"` built a mandate the engine had to refuse, so **every** payment came back `invalid_mandate_cap:perTxMax=10000` — a message naming 10000 as the invalid cap, when 10000 is a perfectly good number and the quotes were the whole problem. It now honours a quoted number (which means exactly one thing) and refuses anything that would need a guess, loudly, saying that the payees and caps in force are not the ones you wrote.
10
+ - **Fail-open, now closed:** both window checks are `now < Date.parse(...)` and `now > Date.parse(...)`, and a date that does not parse yields NaN, which makes both comparisons false. An unreadable window was not a narrow window, it was no window: the mandate never expired. An unenforceable window now denies, at the engine as well as at the override.
11
+ - A quoted cap in `config.json` was discarded silently, so the 2500 default applied and a 5000 payment came back `per_tx_cap_exceeded:5000>2500`, quoting a cap the operator had never set. It now follows the same rule as the environment variables, which had warned since 2026-07-24.
12
+ - `"currency": "usd"` denied every payment with `currency_not_allowed:USD`, naming as forbidden the exact currency it had allowed. ISO 4217 codes are case-insensitive; case was never a policy decision.
13
+
14
+ Nothing was loosened. Caps still cap, a genuinely different currency is still denied, a payee off the allowlist is still denied, and an unreadable value still falls back to the safe default rather than to no limit at all.
15
+
3
16
  ## 0.5.5
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -46,9 +46,11 @@ it never holds funds.
46
46
 
47
47
  ## To everyone who installed Fidacy
48
48
 
49
- Excluding our own test traffic, installs like yours have made about 1,600 real
50
- firewall decisions to date, over 99% of them blocks. Thank you. Two things worth
51
- a minute of your time:
49
+ The Fidacy firewall has taken **3,099 decisions** to date, **98% of them blocks**,
50
+ counted by the engine and public at
51
+ [api.fidacy.com/v1/pulse](https://api.fidacy.com/v1/pulse) if you want to check the
52
+ number yourself. Thank you for being part of it. Two things worth a minute of your
53
+ time:
52
54
 
53
55
  1. **See and claim what YOUR install blocked.** Your install carries a private,
54
56
  anonymous id on your machine (we never learn who you are unless you choose to).
package/dist/index.js CHANGED
@@ -4320,14 +4320,24 @@ function resolveMandateRules(cfg) {
4320
4320
  }
4321
4321
  return n;
4322
4322
  };
4323
- const fileNum = (v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : void 0;
4323
+ const fileNum = (name, v) => {
4324
+ if (v === void 0 || v === null) return void 0;
4325
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v.trim()) : NaN;
4326
+ if (!Number.isFinite(n) || n <= 0) {
4327
+ console.error(
4328
+ `[fidacy] mandate.${name} in config.json is ${JSON.stringify(v)}, which is not a positive number, so it cannot be enforced as a cap. Ignoring it and using the safe default. Write it as a bare JSON number, with no quotes, thousands separator, currency symbol or unit (e.g. "${name}": 2500).`
4329
+ );
4330
+ return void 0;
4331
+ }
4332
+ return n;
4333
+ };
4324
4334
  const payees = envList(process.env.FIDACY_ALLOW_PAYEES) ?? m.payees ?? [];
4325
4335
  return {
4326
4336
  payees: payees.includes(DEMO_PAYEE) ? payees : [...payees, DEMO_PAYEE],
4327
4337
  categories: envList(process.env.FIDACY_ALLOW_CATEGORIES) ?? m.categories ?? ["*"],
4328
4338
  currency: process.env.FIDACY_CURRENCY ?? m.currency ?? "USD",
4329
- perTxMax: envNum("FIDACY_PER_TX_MAX", process.env.FIDACY_PER_TX_MAX) ?? fileNum(m.perTxMax) ?? 2500,
4330
- maxTotal: envNum("FIDACY_MAX_TOTAL", process.env.FIDACY_MAX_TOTAL) ?? fileNum(m.maxTotal) ?? 1e4
4339
+ perTxMax: envNum("FIDACY_PER_TX_MAX", process.env.FIDACY_PER_TX_MAX) ?? fileNum("perTxMax", m.perTxMax) ?? 2500,
4340
+ maxTotal: envNum("FIDACY_MAX_TOTAL", process.env.FIDACY_MAX_TOTAL) ?? fileNum("maxTotal", m.maxTotal) ?? 1e4
4331
4341
  };
4332
4342
  }
4333
4343
 
@@ -4759,6 +4769,13 @@ function validateMandateCaps(mandate) {
4759
4769
  return `invalid_mandate_cap:perTxMax=${String(mandate.allow.perTxMax)}`;
4760
4770
  if (bad(mandate.allow.maxTotal))
4761
4771
  return `invalid_mandate_cap:maxTotal=${String(mandate.allow.maxTotal)}`;
4772
+ if (!mandate.window || typeof mandate.window !== "object")
4773
+ return "invalid_mandate:missing_window";
4774
+ for (const campo of ["notBefore", "notAfter"]) {
4775
+ const v = mandate.window[campo];
4776
+ if (typeof v !== "string" || Number.isNaN(Date.parse(v)))
4777
+ return `invalid_mandate_window:${campo}=${String(v)}`;
4778
+ }
4762
4779
  return null;
4763
4780
  }
4764
4781
  function evaluate(mandate, req, spentSoFar) {
@@ -4772,7 +4789,7 @@ function evaluate(mandate, req, spentSoFar) {
4772
4789
  return "before_mandate_window";
4773
4790
  if (now > Date.parse(mandate.window.notAfter))
4774
4791
  return "after_mandate_window";
4775
- if (req.currency !== mandate.allow.currency)
4792
+ if (req.currency.toUpperCase() !== mandate.allow.currency.toUpperCase())
4776
4793
  return `currency_not_allowed:${req.currency}`;
4777
4794
  if (req.amount <= 0)
4778
4795
  return "non_positive_amount";
@@ -5085,7 +5102,7 @@ var FileAuditStore = class {
5085
5102
  import { statSync } from "node:fs";
5086
5103
 
5087
5104
  // ../mcp/src/telemetry.ts
5088
- var CLIENT_VERSION = true ? "0.5.5" : "dev";
5105
+ var CLIENT_VERSION = true ? "0.5.7" : "dev";
5089
5106
  function bandOf(amount) {
5090
5107
  if (typeof amount !== "number" || !Number.isFinite(amount) || amount <= 0) return void 0;
5091
5108
  if (amount < 10) return "lt10";
@@ -5193,12 +5210,38 @@ async function flush() {
5193
5210
  }
5194
5211
 
5195
5212
  // ../mcp/src/core.ts
5213
+ function normalizaMandatoDeEnv(m) {
5214
+ const allow = m.allow;
5215
+ for (const campo of ["perTxMax", "maxTotal"]) {
5216
+ const v = allow[campo];
5217
+ const n = typeof v === "number" ? v : typeof v === "string" ? Number(v.trim()) : NaN;
5218
+ if (!Number.isFinite(n) || n <= 0) {
5219
+ return `has allow.${campo} = ${JSON.stringify(v)}, which is not a positive number, so that cap cannot be enforced. Write it as a bare JSON number, with no quotes, thousands separator, currency symbol or unit (e.g. "${campo}": 2500).`;
5220
+ }
5221
+ allow[campo] = n;
5222
+ }
5223
+ const window = m.window;
5224
+ for (const campo of ["notBefore", "notAfter"]) {
5225
+ const v = window[campo];
5226
+ if (typeof v !== "string" || Number.isNaN(Date.parse(v))) {
5227
+ return `has window.${campo} = ${JSON.stringify(v)}, which is not a readable date, so the mandate's time window cannot be enforced. Use an ISO 8601 timestamp (e.g. "${campo}": "2026-01-01T00:00:00Z").`;
5228
+ }
5229
+ }
5230
+ return null;
5231
+ }
5196
5232
  function buildMandate() {
5197
5233
  if (process.env.FIDACY_MANDATE_JSON) {
5198
5234
  try {
5199
5235
  const parsed = JSON.parse(process.env.FIDACY_MANDATE_JSON);
5200
- if (parsed && typeof parsed === "object" && parsed.allow && parsed.window) return parsed;
5201
- console.error("[fidacy] FIDACY_MANDATE_JSON is missing allow/window; ignoring it and using the safe default mandate.");
5236
+ if (parsed && typeof parsed === "object" && parsed.allow && parsed.window) {
5237
+ const problema = normalizaMandatoDeEnv(parsed);
5238
+ if (!problema) return parsed;
5239
+ console.error(
5240
+ `[fidacy] FIDACY_MANDATE_JSON ${problema} The override is being IGNORED and the safe default mandate is in force instead, so your payees and caps are NOT the ones you wrote.`
5241
+ );
5242
+ } else {
5243
+ console.error("[fidacy] FIDACY_MANDATE_JSON is missing allow/window; ignoring it and using the safe default mandate.");
5244
+ }
5202
5245
  } catch (err) {
5203
5246
  console.error(`[fidacy] FIDACY_MANDATE_JSON is not valid JSON (${err.message}); ignoring it and using the safe default mandate.`);
5204
5247
  }
@@ -5274,7 +5317,7 @@ function requestUpgrade() {
5274
5317
  }
5275
5318
 
5276
5319
  // ../mcp/src/provision.ts
5277
- var CLIENT_VERSION2 = true ? "0.5.5" : "dev";
5320
+ var CLIENT_VERSION2 = true ? "0.5.7" : "dev";
5278
5321
  function provisionEnabled() {
5279
5322
  const v = (process.env.FIDACY_DISABLE_PROVISION ?? "").trim().toLowerCase();
5280
5323
  return !(v === "1" || v === "true" || v === "yes");
@@ -5417,7 +5460,7 @@ function trialCountdownLine(keyOverride) {
5417
5460
  }
5418
5461
 
5419
5462
  // ../mcp/src/register.ts
5420
- var CLIENT_VERSION3 = true ? "0.5.5" : "dev";
5463
+ var CLIENT_VERSION3 = true ? "0.5.7" : "dev";
5421
5464
  function endpoint3() {
5422
5465
  const base = (process.env.FIDACY_ENGINE_URL ?? "https://api.fidacy.com").replace(/\/$/, "");
5423
5466
  return `${base}/v1/register`;
@@ -3,7 +3,7 @@
3
3
  "name": "Fidacy AI Agent Firewall",
4
4
  "description": "Firewall for AI agents. Watches every action the agent takes and ends each session in a digest anyone can recompute; commands, paths and arguments never leave your machine, only counts and a hash. Blocks wrong/lookalike payee, over-cap and duplicate-invoice fraud before money moves. Non-custodial, local-first, deny-by-default. Anonymous telemetry (counts only) can be disabled with FIDACY_DISABLE_TELEMETRY=1.",
5
5
  "icon": "https://fidacy.com/logo.png",
6
- "version": "0.5.5",
6
+ "version": "0.5.7",
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "request_payment",
@@ -25,43 +25,20 @@
25
25
  "properties": {
26
26
  "operatorEmail": {
27
27
  "type": "string",
28
- "description": "Your email (optional). Ties this agent's protection history to you so it can move into a free account, and lets Fidacy reach you about this install. Never shared; remove any time."
28
+ "description": "Your email (optional). Ties this agent's protection history to you so it can move into a free account, and lets Fidacy reach you about this install. Never shared; remove any time. Your email (optional) Ties this agent's protection history to you so it can move into a free account, and lets Fidacy reach you about this install. Optional — the firewall works without it."
29
29
  },
30
30
  "engineApiKey": {
31
31
  "type": "string",
32
- "description": "Fidacy engine API key (fky_live_/fky_test_) enabling signed verdicts via assess_action. Falls back to FIDACY_ENGINE_API_KEY."
32
+ "description": "Fidacy engine API key (fky_live_/fky_test_) enabling signed verdicts via assess_action. Falls back to FIDACY_ENGINE_API_KEY. Engine API key Free key at app.fidacy.com/signup. Enables signed trust verdicts (assess_action) and keeps the firewall active past the 20-decision anonymous trial."
33
33
  },
34
34
  "engineUrl": {
35
35
  "type": "string",
36
- "description": "Fidacy engine base URL. Defaults to https://api.fidacy.com (or FIDACY_ENGINE_URL)."
36
+ "description": "Fidacy engine base URL. Defaults to https://api.fidacy.com (or FIDACY_ENGINE_URL). Engine URL"
37
37
  },
38
38
  "subject": {
39
39
  "type": "string",
40
- "description": "Mandate subject identity for this agent. Defaults to agent:demo (or FIDACY_SUBJECT)."
40
+ "description": "Mandate subject identity for this agent. Defaults to agent:demo (or FIDACY_SUBJECT). Agent subject"
41
41
  }
42
42
  }
43
- },
44
- "uiHints": {
45
- "operatorEmail": {
46
- "label": "Your email (optional)",
47
- "placeholder": "you@company.com",
48
- "help": "Ties this agent's protection history to you so it can move into a free account, and lets Fidacy reach you about this install. Optional — the firewall works without it."
49
- },
50
- "engineApiKey": {
51
- "label": "Engine API key",
52
- "placeholder": "fky_live_...",
53
- "sensitive": true,
54
- "help": "Free key at app.fidacy.com/signup. Enables signed trust verdicts (assess_action) and keeps the firewall active past the 20-decision anonymous trial."
55
- },
56
- "engineUrl": {
57
- "label": "Engine URL",
58
- "placeholder": "https://api.fidacy.com",
59
- "advanced": true
60
- },
61
- "subject": {
62
- "label": "Agent subject",
63
- "placeholder": "agent:demo",
64
- "advanced": true
65
- }
66
43
  }
67
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/openclaw-plugin",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Fidacy payment firewall as a native OpenClaw plugin: signed, verifiable verdicts on every money-moving agent action, in-process (no MCP subprocess).",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Fidacy (ZeepCode Group Technology LLC) <hello@fidacy.com> (https://fidacy.com)",
@@ -57,7 +57,7 @@
57
57
  "typebox": "1.1.39",
58
58
  "typescript": "^5.6.3",
59
59
  "@fidacy/firewall": "0.1.1",
60
- "@fidacy/mcp": "0.7.2"
60
+ "@fidacy/mcp": "0.7.4"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "node scripts/bundle.mjs",