create-metamynd-agent 0.7.0 → 0.7.2

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 (3) hide show
  1. package/README.md +41 -8
  2. package/index.mjs +67 -27
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -16,6 +16,10 @@ enforcement boundary — see [Separate tool gateway](#separate-tool-gateway-defa
16
16
 
17
17
  ## Free local harness (no account, no network, `--harness`)
18
18
 
19
+ > **Without MetaMynd, you can be bypassed.** `--harness` proves your policy logic works — it does
20
+ > not enforce it against a caller trying to get around it. See [What this is not](#what-this-is-not)
21
+ > below before you rely on it for anything beyond testing rules.
22
+
19
23
  ```bash
20
24
  npm create metamynd-agent@latest -- --harness # or: npx create-metamynd-agent --harness
21
25
  ```
@@ -39,6 +43,8 @@ my-agent/
39
43
 
40
44
  ### What this is not
41
45
 
46
+ **Without MetaMynd, you can be bypassed.** Everything below is why, precisely.
47
+
42
48
  No anchored or cross-party-verifiable identity, no dashboard reachable when your machine is off, no
43
49
  owner queue someone *else* can approve from, no anchored evidence, no enforced platform Standards.
44
50
  That set of things is the hosted platform — and getting there later is a **config change, not a
@@ -81,6 +87,8 @@ agent with your own limits.
81
87
 
82
88
  `--sandbox` always scaffolds the single-process shape (no `gateway/`) — it's a shared identity never
83
89
  meant to hold real credentials, so there's nothing here worth a separate enforcement boundary for.
90
+ Note this is a narrower gap than `--harness`'s: `--sandbox` DOES call MetaMynd for the decision, it
91
+ just still has nowhere else for the tool to live, so the same in-process bypass applies regardless.
84
92
  The generated project's own README says so. The full flow below is what scaffolds one by default.
85
93
 
86
94
  ## Use
@@ -126,24 +134,49 @@ npm start
126
134
 
127
135
  ### Separate tool gateway (default)
128
136
 
137
+ This is the other half of **without MetaMynd, you can be bypassed**: WITH it — specifically, with
138
+ `gateway/`, the second process this scaffolds by default — calling the tool directly instead of
139
+ through the check no longer works, the same way the hosted platform's own MCP counterparty can't
140
+ be talked around by a compromised agent. See [Not solved by the gateway](#not-solved-by-the-gateway)
141
+ below for the two things this specifically does **not** cover.
142
+
129
143
  `guard.guardTool()` in `index.mjs` still runs — it's a fast, local, client-side pre-check that gives
130
144
  good UX (fail fast on an obviously-blocked call, no round trip) — but it is **not** what stops a
131
145
  bypass. It still calls its handler in the SAME process regardless of where the decision came from,
132
146
  so anything able to call that handler directly gets the same result the gate would have given it.
133
147
 
134
- What actually stops a bypass is that `bookFlight()` doesn't exist in the agent's process at all.
148
+ What actually stops that bypass is that `bookFlight()` doesn't exist in the agent's process at all.
135
149
  It exists only in `gateway/server.mjs` — a separate process, started separately, holding any real
136
150
  tool credentials the agent process never sees — which independently re-verifies every request
137
- against the agent's own published policy bundle before running it (same shape as the mutual
138
- counterparty check in [`@metamynd/agentsafe-mcp-guard`](https://www.npmjs.com/package/@metamynd/agentsafe-mcp-guard),
139
- built with [`@metamynd/agentsafe-http-gateway`](https://www.npmjs.com/package/@metamynd/agentsafe-http-gateway)).
140
- It's a minimal slice of the same pattern proven end to end in `demo/duffel-mcp-gateway` in the
151
+ against the agent's own published policy bundle before running it, and **binds that request to the
152
+ actual body being executed** (`@metamynd/agentsafe-http-gateway` ≥ 0.2.0) — closing a confused-deputy
153
+ gap found during testing, where a signed cheap request could be governed while a different, expensive
154
+ body was the one that actually ran. Same shape as the mutual counterparty check in
155
+ [`@metamynd/agentsafe-mcp-guard`](https://www.npmjs.com/package/@metamynd/agentsafe-mcp-guard), built
156
+ with [`@metamynd/agentsafe-http-gateway`](https://www.npmjs.com/package/@metamynd/agentsafe-http-gateway).
157
+ It's a minimal slice of the fuller pattern proven end to end in `demo/duffel-mcp-gateway` in the
141
158
  AgentSafe repo (mutual handshake, x402 payment binding, capability tokens) — this scaffold gives you
142
- just the part that closes the bypass, not the whole protocol.
159
+ just the part that closes the direct-call bypass, not the whole protocol.
160
+
161
+ #### Not solved by the gateway
162
+
163
+ Named precisely, not left implicit:
164
+
165
+ - **Replay.** The gateway checks a signed request is fresh, not that it hasn't been used before —
166
+ a captured valid request can be resent within the freshness window. Single-use nonce consumption
167
+ is the stateful issuer gate's job (`POST /policy/mandate/authorize`); this scaffold never calls it.
168
+ - **Cumulative spend.** Each call is checked against the per-transaction cap correctly, but the
169
+ mandate's TOTAL budget isn't tracked at the gateway — many separately-legal calls can still add
170
+ up past it. The real total is only enforced where a hold is actually reserved: the issuer gate.
171
+
172
+ Both require wiring the full authorize-before-execute flow (agent calls the stateful gate first,
173
+ gateway checks a decision bound to that specific authorization) rather than per-request policy
174
+ re-evaluation alone — see `gateway/README.md`'s own "Beyond this minimal slice" section.
143
175
 
144
176
  Pass `--no-gateway` to opt out and get the old single-process scaffold instead — e.g. if you're
145
- already running your own separate gateway and don't need this one. The generated project's own
146
- README says plainly that this is *not* a separate enforcement boundary if you do.
177
+ already running your own separate gateway and don't need this one. **You are back to being
178
+ bypassable if you do**, for the same structural reason `--harness` is; the generated project's own
179
+ README says so plainly.
147
180
 
148
181
  ## Non-interactive
149
182
 
package/index.mjs CHANGED
@@ -28,7 +28,9 @@ const GUARD_VERSION = '^0.5.0';
28
28
  const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
29
29
  const MCP_GUARD_VERSION = '^0.1.0';
30
30
  const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
31
- const GATEWAY_VERSION = '^0.1.0';
31
+ // 0.2.0 fixes a confused-deputy gap (payload not bound to the signed request) — the CLI must
32
+ // never scaffold a range that could resolve below it.
33
+ const GATEWAY_VERSION = '^0.2.0';
32
34
  const DEFAULT_API = 'https://metamynd.ai/api/v1';
33
35
  const DEFAULT_GATEWAY_PORT = 4401; // distinct from --harness's dashboard (4400)
34
36
 
@@ -420,6 +422,10 @@ console.log(dim(' - the blocked call never reached your tool at all.'));
420
422
  console.log(dim(' - every decision was recorded as tamper-evident evidence.'));
421
423
  console.log(dim(' - if the gate were unreachable the guard fails CLOSED: it blocks.'));
422
424
  console.log('');
425
+ console.log(bold(' Without MetaMynd, you can be bypassed.') + ' bookFlight() runs in THIS process -');
426
+ console.log(dim(' call it directly instead of gatedBookFlight and nothing above stops you.'));
427
+ console.log(dim(' Re-scaffold without --sandbox/--no-gateway for the default shape, which does.'));
428
+ console.log('');
423
429
  console.log(' Change the cap in the dashboard (Legal Entity -> SOPs) and run again.');
424
430
  console.log(dim(' The outcome changes. This file does not. That is the point.'));
425
431
  console.log('');
@@ -600,6 +606,9 @@ console.log(dim(' authority, because it cannot name an action nobody delegat
600
606
  console.log(dim(' - every blocked/escalated call never reached a real tool at all.'));
601
607
  console.log(dim(' - if the gate were unreachable the guard fails CLOSED: it blocks.'));
602
608
  console.log('');
609
+ console.log(bold(' With MetaMynd, you can\\'t be bypassed.') + ' ./gateway is why - it independently');
610
+ console.log(dim(' re-verified step 1 before running it, and holds the tool this file never can.'));
611
+ console.log('');
603
612
  console.log(' Change the cap in the dashboard (Legal Entity -> SOPs) and run again.');
604
613
  console.log(dim(' The outcome changes. This file does not. That is the point.'));
605
614
  console.log('');
@@ -682,23 +691,27 @@ A MetaMynd/AgentSafe-governed agent, scaffolded with \`create-metamynd-agent\`.
682
691
 
683
692
  ${gatewaySection}${
684
693
  withGateway
685
- ? `This scaffold's default shape (agent + separate gateway process, port ${gatewayPort} by
686
- default) is the actual enforcement boundary: \`guard.guardTool()\` in \`index.mjs\` is a
687
- client-side convenience, not a boundary it still runs its handler in-process regardless of
688
- where the decision came from. What actually stops a bypass is that \`bookFlight()\` itself only
689
- exists in \`./gateway\`, a process this one cannot reach into, which independently re-verifies
690
- every request against this agent's own policy bundle. Re-scaffold with \`--no-gateway\` for the
691
- old single-process shape it is NOT a separate enforcement boundary; see its own generated
692
- README for why.`
693
- : `This scaffold has no separate gateway process (either \`--sandbox\`, which never
694
- provisions real credentials, or \`--no-gateway\` was passed): \`guard.guardTool()\` wraps a tool
695
- in the SAME process as the check itself. That is a client-side convenience, not a boundary — it
696
- still runs your tool's handler in-process regardless of where the decision came from, so
697
- anything able to call \`bookFlight()\` directly gets the same result the gate would have given
698
- it. If this tool ever holds a real credential, provision for real (drop \`--sandbox\`) without
699
- \`--no-gateway\` for the default shape, which puts the tool behind a separate process instead.
700
- This is the same structural gap \`--harness\`'s README documents, for the same reason: a
701
- cooperative in-process check has no counterparty to disagree with a caller that skips it.`
694
+ ? `**With MetaMynd's gateway, calling the tool directly and skipping the check no longer
695
+ works** that's what this section is about. This scaffold's default shape (agent + separate
696
+ gateway process, port ${gatewayPort} by default) is the actual enforcement boundary for that
697
+ specific bypass: \`guard.guardTool()\` in \`index.mjs\` is a client-side convenience, not a
698
+ boundary it still runs its handler in-process regardless of where the decision came from. What
699
+ actually stops it is that \`bookFlight()\` itself only exists in \`./gateway\`, a process this one
700
+ cannot reach into, which independently re-verifies every request against this agent's own policy
701
+ bundle AND binds it to the actual body being executed. It does NOT track replay or cumulative
702
+ spend across calls see \`./gateway/README.md\`'s "Beyond this minimal slice" section. Re-scaffold
703
+ with \`--no-gateway\` for the old single-process shape — it is NOT a separate enforcement boundary
704
+ at all; see its own generated README for why.`
705
+ : `**Without MetaMynd, you can be bypassed** this is that case. This scaffold has no
706
+ separate gateway process (either \`--sandbox\`, which never provisions real credentials, or
707
+ \`--no-gateway\` was passed): \`guard.guardTool()\` wraps a tool in the SAME process as the check
708
+ itself. That is a client-side convenience, not a boundary it still runs your tool's handler
709
+ in-process regardless of where the decision came from, so anything able to call \`bookFlight()\`
710
+ directly gets the same result the gate would have given it. If this tool ever holds a real
711
+ credential, provision for real (drop \`--sandbox\`) without \`--no-gateway\` for the default
712
+ shape, which puts the tool behind a separate process instead. This is the same structural gap
713
+ \`--harness\`'s README documents, for the same reason: a cooperative in-process check has no
714
+ counterparty to disagree with a caller that skips it.`
702
715
  }
703
716
 
704
717
  ## Change the rules
@@ -828,7 +841,11 @@ function gatewayGitignore() {
828
841
  function gatewayReadme(slug, scope, port) {
829
842
  return `# ${slug}-gateway
830
843
 
831
- This is the **real enforcement boundary** for \`${slug}\`'s tool(s) not \`../index.mjs\`.
844
+ **With MetaMynd, you can't call the tool directly and skip the check.** This process is why. It
845
+ is the **real enforcement boundary** for \`${slug}\`'s tool(s) — not \`../index.mjs\` — for that
846
+ specific bypass. See [Beyond this minimal slice](#beyond-this-minimal-slice) below for the two
847
+ things it does NOT yet close on its own: replaying a captured request, and spend adding up
848
+ across many separately-legal calls.
832
849
 
833
850
  ## Why this exists
834
851
 
@@ -873,10 +890,26 @@ add another protected route here rather than adding a local function back in \`i
873
890
 
874
891
  ## Beyond this minimal slice
875
892
 
876
- This gateway only re-verifies a signed request (§9.3/§9.6 of the MAGP spec). It does not do the
877
- mutual DID handshake, x402 payment binding, or commitment-bound capability tokens that a
878
- production Service integration would add see \`@metamynd/agentsafe-mcp-guard\`'s own README for
879
- those, and \`demo/duffel-mcp-gateway\` in the AgentSafe repo for a full worked example.
893
+ This gateway independently re-verifies a signed request AND binds it to the actual request body
894
+ (§9.3/§9.6 of the MAGP spec, plus payload binding \`@metamynd/agentsafe-http-gateway\` 0.2.0).
895
+ That closes the specific bypass this scaffold exists to close: an agent (or anything able to call
896
+ its code) presenting one set of values while a different set actually executes.
897
+
898
+ Two things it deliberately does NOT close, named precisely rather than left implicit:
899
+
900
+ - **Replay.** \`verifyRequest()\` checks the signed request is fresh, not that it hasn't been used
901
+ before — a captured valid request can be resent within the freshness window. Single-use nonce
902
+ consumption is the stateful issuer gate's job, not this gateway's; this scaffold never calls it.
903
+ - **Cumulative spend.** Each call is checked against the per-transaction cap correctly, but the
904
+ mandate's TOTAL budget isn't tracked here — many separately-legal calls can still add up past
905
+ it. The real total is only enforced where holds are actually reserved: \`POST
906
+ /policy/mandate/authorize\` on the issuer.
907
+
908
+ Closing both means wiring the full authorize-before-execute flow — the agent calling that
909
+ stateful endpoint first, this gateway checking a decision bound to that specific authorization —
910
+ not just re-evaluating policy per request. See \`@metamynd/agentsafe-mcp-guard\`'s own README
911
+ (mutual DID handshake, x402 payment binding, commitment-bound capability tokens) and
912
+ \`demo/duffel-mcp-gateway\` in the AgentSafe repo for what that full pattern looks like.
880
913
  `;
881
914
  }
882
915
 
@@ -1552,12 +1585,17 @@ console.log(dim(' authority, because it cannot name an action nobody delegat
1552
1585
  console.log(dim(' - the blocked call never reached your tool at all.'));
1553
1586
  console.log(dim(' - every decision is in ./metamynd-harness.log.jsonl - yours, locally.'));
1554
1587
  console.log('');
1588
+ console.log(bold(' Without MetaMynd, you can be bypassed.') + ' bookFlight() above runs in THIS');
1589
+ console.log(dim(' process - call it directly instead of gatedBookFlight and nothing stops you.'));
1590
+ console.log(dim(' --harness proves your policy logic; it does not enforce it against that.'));
1591
+ console.log('');
1555
1592
  console.log(' Edit ./metamynd-rules.json (or the dashboard) and run again - the outcome');
1556
1593
  console.log(dim(' changes. This file does not. That is the point.'));
1557
1594
  console.log('');
1558
1595
  console.log(dim(' Ready for more than one machine, a queue someone else can approve from,'));
1559
- console.log(dim(' anchored evidence, or KYC/KYB-backed identity? That is the hosted platform -'));
1560
- console.log(dim(' same guardTool() call, same rules shape, drop --harness and provision there.'));
1596
+ console.log(dim(' anchored evidence, or KYC/KYB-backed identity, AND a separate gateway process'));
1597
+ console.log(dim(' that closes the bypass above? That is the hosted platform - drop --harness'));
1598
+ console.log(dim(' and provision there; the same guardTool() call keeps working.'));
1561
1599
  console.log('');
1562
1600
  dashboard.close();
1563
1601
  `;
@@ -1607,6 +1645,8 @@ open the dashboard to approve it), and a BLOCK (an action outside the mandate en
1607
1645
 
1608
1646
  ## What this is not
1609
1647
 
1648
+ **Without MetaMynd, you can be bypassed.** Everything below is why, precisely.
1649
+
1610
1650
  No anchored/verifiable identity, no cross-party trust, no evidence anyone but you can audit,
1611
1651
  no dashboard reachable when this machine is off, no owner queue someone else can approve from.
1612
1652
  That's the hosted platform (\`npx create-metamynd-agent\`, without \`--harness\`) — same
@@ -1617,8 +1657,8 @@ cooperative library this process embeds — call the tool handler directly inste
1617
1657
  one and nothing stops you, because there is no second party in the loop to disagree with you.
1618
1658
  That's structural, not a bug: use this harness to govern your own agent's own honest behavior,
1619
1659
  not as a defense against an agent (or a person) actively trying to get around it. The hosted
1620
- platform's \`guardTool()\` doesn't have this gap, because the MCP/tool service re-verifies the
1621
- agent's signed authority for itself instead of trusting that the agent's own guard ran.
1660
+ platform's default scaffold doesn't have this gap, because a SEPARATE gateway process re-verifies
1661
+ the agent's signed authority for itself instead of trusting that the agent's own guard ran.
1622
1662
  `;
1623
1663
  }
1624
1664
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-metamynd-agent",
3
- "version": "0.7.0",
4
- "description": "Scaffold a MetaMynd/AgentSafe-governed AI agent in one command — logs in, provisions the agent (identity + mandate + SOP + Standards) in a single call, writes agent.metamynd.json plus a runnable agent + separate tool-gateway process (the real enforcement boundary). --harness scaffolds a free, local, zero-network governance harness instead.",
3
+ "version": "0.7.2",
4
+ "description": "Scaffold a MetaMynd/AgentSafe-governed AI agent in one command — logs in, provisions the agent (identity + mandate + SOP + Standards) in a single call, writes agent.metamynd.json plus a runnable agent + separate tool-gateway process that closes the direct-call bypass. --harness scaffolds a free, local, zero-network governance harness instead.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-metamynd-agent": "index.mjs"