@fidacy/openclaw-plugin 0.5.6 → 0.5.8

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,31 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 59bd236: Complete and reconcile the privacy disclosure. The ClawHub security audit put this plugin in Review for "inconsistent privacy disclosures about local storage and outbound metadata", and it was right on both counts.
8
+
9
+ - **Local storage.** The README claimed local state was `config.json` plus `audit/audit.log` and ended with "Nothing else". `~/.fidacy/pending-anchors.jsonl` was never disclosed. A separate line promised a session log at `~/.fidacy/sessions/`, which belongs to the MCP-plus-hooks install and not to this plugin. Both are now stated once, accurately, as a complete list.
10
+ - **Outbound metadata.** The README named two network calls. The plugin makes five: `/v1/telemetry`, `/v1/provision`, `/v1/artifacts`, `/v1/assess` and `/v1/register`. All five are now listed with what each one sends and what turns it off, in one place instead of two copies that had drifted apart.
11
+ - **The static-analysis flag.** `suspicious.env_credential_access` is described accurately now: reading your key and sending it to your engine is what a bearer token is, and every tool that accepts an API key matches that pattern. The old text gave a different and wrong reason ("the identifier is named like a secret"), in a second copy that contradicted the first.
12
+ - The "verify it yourself" pointer aimed at a mirror repo while releases are provenance-linked to `lucaslubi/fidacy`. It now points where the code actually is, so the instruction works.
13
+
14
+ No behaviour changed. This release is documentation only, and every claim in it was checked against the code rather than against the previous README.
15
+
16
+ ## 0.5.7
17
+
18
+ ### Patch Changes
19
+
20
+ - Four configuration mistakes that presented themselves as security verdicts, so the firewall looked broken and stopped real payments for reasons nobody could see.
21
+
22
+ - `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.
23
+ - **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.
24
+ - 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.
25
+ - `"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.
26
+
27
+ 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.
28
+
3
29
  ## 0.5.5
4
30
 
5
31
  ### Patch Changes
package/README.md CHANGED
@@ -15,16 +15,35 @@ a hash digest anyone can recompute.
15
15
  Stays on your machine, always:
16
16
 
17
17
  - the command, file path and arguments of every tool call
18
- - the full session log (`~/.fidacy/sessions/`), which is yours to delete
19
- - your mandate and config (`~/.fidacy/`)
20
-
21
- Leaves your machine:
22
-
23
- - per-category counts (for example `shell: 9, file: 4`) and the session digest,
24
- anchored via the Fidacy engine when the install is activated
25
- - anonymous telemetry events (install, session, decision counts; a random UUID,
26
- never a name or hostname). Disable with `FIDACY_DISABLE_TELEMETRY=1`.
27
- - your email, only if you set `operatorEmail`, which is opt-in and removable
18
+ - your mandate and config (`~/.fidacy/`), which is yours to delete
19
+
20
+ Written to disk, and this is the complete list:
21
+
22
+ - `~/.fidacy/config.json` (mode 0600): the anonymous install id, tier, and your
23
+ mandate rules
24
+ - `~/.fidacy/audit/audit.log`: the hash-chained decision log
25
+ - `~/.fidacy/pending-anchors.jsonl`: session digests waiting to be anchored,
26
+ written only while an anchor is queued and cleared once it lands
27
+
28
+ Leaves your machine, and this is the complete list of hosts and paths. Every one
29
+ goes to your configured engine (`https://api.fidacy.com` by default) and nowhere
30
+ else:
31
+
32
+ - `/v1/telemetry`: anonymous usage events (install, session, decision counts; a
33
+ random UUID, never a name or hostname). Off with `FIDACY_DISABLE_TELEMETRY=1`.
34
+ - `/v1/provision`: a background free-key request. Off with
35
+ `FIDACY_DISABLE_PROVISION=1`.
36
+ - `/v1/artifacts`: per-category counts (for example `shell: 9, file: 4`) and the
37
+ session digest, plus any hash you anchor with `anchor_artifact`. Hashes and
38
+ counts only; no file content and no command text.
39
+ - `/v1/assess`: only when you call `assess_action` yourself, and only if you set
40
+ an engine API key. Sends the action you passed to the tool.
41
+ - `/v1/register`: only if you set `operatorEmail`, which is opt-in and removable.
42
+ Sends that email and nothing else.
43
+
44
+ With `FIDACY_DISABLE_TELEMETRY=1`, `FIDACY_DISABLE_PROVISION=1`, no engine key
45
+ and no `operatorEmail`, the plugin makes no network calls at all and gates
46
+ payments entirely offline.
28
47
 
29
48
  ## The tools
30
49
 
@@ -103,22 +122,32 @@ Verify any verdict yourself: https://api.fidacy.com/.well-known/jwks.json
103
122
 
104
123
  This is a payment-control plugin, so audit it like one. What it actually does:
105
124
 
106
- - **Network calls it makes:** anonymous usage telemetry to `api.fidacy.com/v1/telemetry`
107
- (an install UUID plus event enums; never payee, amount, currency, or content) and a
108
- background free-key provision to `/v1/provision`. Both are opt-out:
109
- `FIDACY_DISABLE_TELEMETRY=1` and `FIDACY_DISABLE_PROVISION=1`. Neither is ever on
110
- the decision path; payment gating works fully offline.
111
- - **Local state:** `~/.fidacy/config.json` (0600; anon id, tier, your mandate rules)
112
- and `~/.fidacy/audit/audit.log` (the hash-chained decision log). Nothing else.
125
+ - **Network calls it makes:** the five endpoints listed under "what leaves your
126
+ machine" above, all on your configured engine and nowhere else. That list is
127
+ stated once, at the top, on purpose: this section used to carry a second,
128
+ shorter copy naming only two of them, and a privacy disclosure that
129
+ contradicts itself is worse than a long one. None of the five is ever on the
130
+ decision path; payment gating works fully offline.
131
+ - **Local state:** the three files listed above, and nothing else. This bullet
132
+ used to name two of them and end with "Nothing else", which was not true:
133
+ `pending-anchors.jsonl` was never disclosed. Delete `~/.fidacy` to clear all
134
+ of it.
113
135
  - **`FIDACY_SIGNING_KEY_B64`:** an OPTIONAL env var the operator sets to pin a
114
136
  stable local Ed25519 signing key for grants. If unset, a per-session key is
115
137
  generated. The plugin reads it; it never writes or transmits it.
116
138
  - **API keys:** read from your plugin config (`engineApiKey`) or the
117
139
  `FIDACY_ENGINE_API_KEY` env var, sent only to your configured engine URL
118
140
  (default `api.fidacy.com`) as a bearer token. There are no hardcoded keys or
119
- secrets in the published bundle; scanner hits on those lines are pattern matches
120
- on the env/config reads above, and you can verify yourself: the bundle is
121
- unminified on npm and mirrored at github.com/lucaslubi/fidacy-mcp (plugin-native/).
141
+ secrets in the published bundle. A static analyser will flag this as
142
+ `suspicious.env_credential_access` ("environment variable access combined with
143
+ network send"), and that description is accurate: reading your key and sending
144
+ it to your engine is what a bearer token IS, and every tool that takes an API
145
+ key matches the same pattern. What the rule cannot tell you is WHOSE key and
146
+ WHICH host, so check both yourself: the bundle ships unminified, the source is
147
+ at github.com/lucaslubi/fidacy (`packages/openclaw-plugin`), and every release
148
+ is provenance-linked to the commit it was built from. The command below settles
149
+ the "hardcoded secret" question in one line, and the test suite fails the build
150
+ if a literal ever gets in.
122
151
  - **Verify our signatures, not our word:** every verdict checks against the public
123
152
  JWKS at `api.fidacy.com/.well-known/jwks.json` with the open-source
124
153
  [`@fidacy/verify`](https://www.npmjs.com/package/@fidacy/verify).
@@ -141,15 +170,14 @@ published package, in any version. Check it yourself:
141
170
  cd $(mktemp -d) && curl -sL $(npm view @fidacy/openclaw-plugin dist.tarball) | tar xz && grep -rE "fky_(live|test)_[A-Za-z0-9]{8,}|BEGIN [A-Z ]*PRIVATE KEY" package/ || echo "no credential literals found"
142
171
  ```
143
172
 
144
- A static scanner may flag the line that resolves your credential as a hardcoded secret,
145
- because the identifier on the right of the assignment is named like one. It reads your
146
- config and falls back to the environment, defaulting to the empty string. The command
147
- above is what settles the question, and the test suite fails the build if a literal ever
148
- does get in.
173
+ On the `suspicious.env_credential_access` finding a static analyser reports here, see
174
+ the "API keys" bullet above. It is stated in one place rather than two, because the
175
+ two copies had drifted into giving different reasons for the same flag.
149
176
 
150
177
  **Turning the network off.** `FIDACY_DISABLE_TELEMETRY=1` stops anonymous install and
151
178
  usage telemetry. `FIDACY_DISABLE_PROVISION=1` stops the background free-key provisioning.
152
- With both set and no engine credential, the plugin decides locally and contacts nothing.
179
+ With both set, no engine credential and no `operatorEmail`, the plugin decides locally
180
+ and contacts nothing: those four switches cover all five endpoints listed at the top.
153
181
 
154
182
  **Where the history lives.** Decisions go to a hash-chained log under `~/.fidacy` on your
155
183
  machine. That local chain is what makes `spend_summary`, `list_decisions` and
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.6" : "dev";
5105
+ var CLIENT_VERSION = true ? "0.5.8" : "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.6" : "dev";
5320
+ var CLIENT_VERSION2 = true ? "0.5.8" : "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.6" : "dev";
5463
+ var CLIENT_VERSION3 = true ? "0.5.8" : "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.6",
6
+ "version": "0.5.8",
7
7
  "contracts": {
8
8
  "tools": [
9
9
  "request_payment",
@@ -25,19 +25,19 @@
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. 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 \u2014 the firewall works without it."
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."
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. 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."
32
+ "description": "Fidacy engine API key (fky_live_/fky_test_) enabling signed verdicts via assess_action. Falls back to FIDACY_ENGINE_API_KEY."
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). Engine URL"
36
+ "description": "Fidacy engine base URL. Defaults to https://api.fidacy.com (or FIDACY_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). Agent subject"
40
+ "description": "Mandate subject identity for this agent. Defaults to agent:demo (or FIDACY_SUBJECT)."
41
41
  }
42
42
  }
43
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fidacy/openclaw-plugin",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
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.3"
60
+ "@fidacy/mcp": "0.7.4"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "node scripts/bundle.mjs",