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.
- package/README.md +41 -8
- package/index.mjs +67 -27
- 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
|
|
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
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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.
|
|
146
|
-
|
|
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
|
-
|
|
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
|
-
?
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
|
|
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
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
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
|
|
1560
|
-
console.log(dim('
|
|
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
|
|
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.
|
|
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
|
|
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"
|