create-metamynd-agent 0.12.0 → 0.12.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 +2 -0
- package/index.mjs +59 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ enforcement boundary — see [Separate tool gateway](#separate-tool-gateway-defa
|
|
|
13
13
|
> **Prerequisite:** an agent is always owned by a person/org with a **MetaMynd account**. Verification
|
|
14
14
|
> is *reported*, not required, to get started: an unverified owner works on Testnet within the
|
|
15
15
|
> free-tier limits, and verifying once in the dashboard (KYC/KYB) is what unlocks mainnet.
|
|
16
|
+
> This CLI always provisions **Testnet** agents; a mainnet agent is launched from the dashboard's
|
|
17
|
+
> **Launchpad** (`/dashboard/launchpad`) once you are verified.
|
|
16
18
|
|
|
17
19
|
## Free local harness (no account, no network, `--harness`)
|
|
18
20
|
|
package/index.mjs
CHANGED
|
@@ -102,7 +102,7 @@ const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
|
102
102
|
// 0.6.0 brings buildAuthMessage's `resource` field and buildLocalDecisionMessage into this
|
|
103
103
|
// package's own bundled policy-core.mjs (alongside the guard's own 0.10.0) — no scaffolded
|
|
104
104
|
// template code changes, but the floor must still cover the real current version.
|
|
105
|
-
const MCP_GUARD_VERSION = '^0.
|
|
105
|
+
const MCP_GUARD_VERSION = '^0.12.0';
|
|
106
106
|
const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
107
107
|
// 0.2.0 fixes a confused-deputy gap (payload not bound to the signed request) — the CLI must
|
|
108
108
|
// never scaffold a range that could resolve below it.
|
|
@@ -205,7 +205,7 @@ ${c.b('Options')}
|
|
|
205
205
|
--gateway --harness only: ALSO scaffold a second local process (still zero
|
|
206
206
|
network, zero account) that independently re-verifies every request
|
|
207
207
|
against the same rules file, using the real @metamynd/agentsafe-mcp-guard.
|
|
208
|
-
Off by default.
|
|
208
|
+
Off by default. Refuses a replayed request; does not track cumulative spend — see the
|
|
209
209
|
generated README#--gateway for exactly what it does and does not.
|
|
210
210
|
--gateway-port <n> The gateway process's port, hosted flow or --harness --gateway (default 4401)
|
|
211
211
|
--port <n> --harness only: the local dashboard's port (default 4400)
|
|
@@ -216,6 +216,12 @@ ${c.b('Options')}
|
|
|
216
216
|
${c.b('Environment')}
|
|
217
217
|
METAMYND_API, METAMYND_EMAIL, METAMYND_PASSWORD — fallbacks for the flags above
|
|
218
218
|
|
|
219
|
+
${c.b('Network')}
|
|
220
|
+
Every hosted agent this CLI provisions is a ${c.b('Testnet')} agent, whatever your verification status —
|
|
221
|
+
there is deliberately no mainnet flag. To launch a ${c.b('Mainnet')} agent, use the Launchpad in the
|
|
222
|
+
dashboard (https://metamynd.ai/dashboard/launchpad): its Mainnet option is available once your
|
|
223
|
+
owner verification (KYC/KYB) is complete.
|
|
224
|
+
|
|
219
225
|
${c.b('What it does')}
|
|
220
226
|
1. Logs in as a KYB-verified owner → owner access token
|
|
221
227
|
2. POST /onboarding/agent (one call) → identity + mandate + SOP + Standards
|
|
@@ -2319,12 +2325,14 @@ function harnessRulesFile(mandate, sopDocument) {
|
|
|
2319
2325
|
// that skips its own guardToolLocal() call, or calls bookFlight() directly, gets nothing —
|
|
2320
2326
|
// the tool only runs in the gateway process now.
|
|
2321
2327
|
//
|
|
2328
|
+
// Replay of the SAME signed request is refused too (REPLAY_DETECTED): the gateway remembers each nonce for as long as
|
|
2329
|
+
// the guard would accept it, in memory (a restart forgets them). An independent tester's rerun (2026-09-24) found the
|
|
2330
|
+
// same bytes accepted twice before this.
|
|
2331
|
+
//
|
|
2322
2332
|
// What this does NOT close (be precise, this is a local demo, not the hosted platform):
|
|
2323
|
-
//
|
|
2324
|
-
//
|
|
2325
|
-
//
|
|
2326
|
-
// this gateway does per-request re-evaluation only, same as the hosted gateway's baseline
|
|
2327
|
-
// before `requireAuthorization` is added. The README says so.
|
|
2333
|
+
// cumulative spend across many DIFFERENT calls. That needs a STATEFUL authority — the hosted gate's
|
|
2334
|
+
// `requireAuthorization` claims a real, single-use authorizationId against a database and holds the budget there. A
|
|
2335
|
+
// local harness has no such database (that is the whole point of --harness). The README says so.
|
|
2328
2336
|
|
|
2329
2337
|
function harnessGatewayServerFile(scope, gatewayPort, agentDid, neutral = false) {
|
|
2330
2338
|
return `#!/usr/bin/env node
|
|
@@ -2415,9 +2423,35 @@ function readBody(req) {
|
|
|
2415
2423
|
});
|
|
2416
2424
|
}
|
|
2417
2425
|
|
|
2426
|
+
// Replay: every signed request carries a fresh nonce, and the guard refuses one more than 5 minutes old. So a nonce
|
|
2427
|
+
// only has to be remembered for that long to refuse the SAME signed request twice. Checked and recorded in one
|
|
2428
|
+
// synchronous step, before anything awaits, so two simultaneous copies cannot both get through. In memory: restarting
|
|
2429
|
+
// this process forgets it (a replay inside the 5-minute window after a restart is not caught). Cumulative spend across
|
|
2430
|
+
// many DIFFERENT requests is still not tracked here — that needs the hosted gate.
|
|
2431
|
+
const NONCE_TTL_MS = 6 * 60 * 1000;
|
|
2432
|
+
const seenNonces = new Map(); // agentDid|nonce -> expiry
|
|
2433
|
+
function firstUseOfNonce(headers) {
|
|
2434
|
+
let signed;
|
|
2435
|
+
try { signed = JSON.parse(headers['x-magp-request'] ?? 'null'); } catch { return true; } // malformed: the guard refuses it
|
|
2436
|
+
if (!signed || typeof signed.nonce !== 'string') return true; // no nonce: the guard refuses it
|
|
2437
|
+
const now = Date.now();
|
|
2438
|
+
if (seenNonces.size > 10000) for (const [k, exp] of seenNonces) if (exp <= now) seenNonces.delete(k);
|
|
2439
|
+
const key = String(signed.agentDid) + '|' + signed.nonce;
|
|
2440
|
+
const exp = seenNonces.get(key);
|
|
2441
|
+
if (exp !== undefined && exp > now) return false;
|
|
2442
|
+
seenNonces.set(key, now + NONCE_TTL_MS);
|
|
2443
|
+
return true;
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2418
2446
|
const server = http.createServer(async (req, res) => {
|
|
2419
2447
|
try {
|
|
2420
2448
|
const rawBody = await readBody(req);
|
|
2449
|
+
if (!firstUseOfNonce(req.headers)) {
|
|
2450
|
+
console.log('[harness-gateway] BLOCK ' + req.url + ' — REPLAY_DETECTED (this exact signed request already came through)');
|
|
2451
|
+
res.writeHead(403, { 'content-type': 'application/json', 'x-agentsafe-decision': 'block' });
|
|
2452
|
+
res.end(JSON.stringify({ decision: 'block', reasonCode: 'REPLAY_DETECTED' }));
|
|
2453
|
+
return;
|
|
2454
|
+
}
|
|
2421
2455
|
const result = await gateway({ method: req.method, path: req.url, headers: req.headers, rawBody });
|
|
2422
2456
|
const headers = { 'content-type': 'application/json' };
|
|
2423
2457
|
const decision = result.governance?.decision;
|
|
@@ -2978,9 +3012,9 @@ console.log(bold(' Checked twice, by two processes.') + ' bookFlight() lives in
|
|
|
2978
3012
|
console.log(dim(' not here. It independently re-verified every attempt above against the SAME'));
|
|
2979
3013
|
console.log(dim(' ./metamynd-rules.json, over a signed request, before running your tool.'));
|
|
2980
3014
|
console.log('');
|
|
2981
|
-
console.log(dim(' What --gateway does NOT close:
|
|
2982
|
-
console.log(dim(' calls.
|
|
2983
|
-
console.log(dim('
|
|
3015
|
+
console.log(dim(' It also refuses a replay of the same signed request. What --gateway does NOT close:'));
|
|
3016
|
+
console.log(dim(' cumulative spend across many different calls. That needs a STATEFUL authority (the'));
|
|
3017
|
+
console.log(dim(' hosted gate holds the budget in a database) - a local harness has none.'));
|
|
2984
3018
|
console.log(dim(' See README.md#--gateway for exactly what this does and does not prove.'));
|
|
2985
3019
|
console.log('');
|
|
2986
3020
|
console.log(' Edit ./metamynd-rules.json (or the dashboard) and run again - the outcome');
|
|
@@ -3175,9 +3209,8 @@ console.log(dim(' ./metamynd-rules.json, over a signed request, before running
|
|
|
3175
3209
|
console.log(dim(' It enforces scope and identity firmly; the rule inputs above are NOT signed, so an'));
|
|
3176
3210
|
console.log(dim(' agent that can sign could omit or forge one and its rule would not fire.'));
|
|
3177
3211
|
console.log('');
|
|
3178
|
-
console.log(dim('
|
|
3179
|
-
console.log(dim('
|
|
3180
|
-
console.log(dim(' harness has none. See README.md#--gateway for exactly what this does and does not prove.'));
|
|
3212
|
+
console.log(dim(' It also refuses a replay of the same signed request (remembered in memory, so a restart'));
|
|
3213
|
+
console.log(dim(' forgets). See README.md#--gateway for exactly what this does and does not prove.'));
|
|
3181
3214
|
console.log('');
|
|
3182
3215
|
console.log(' Edit ./metamynd-rules.json (or the dashboard) and run again - the outcome');
|
|
3183
3216
|
console.log(dim(' changes, in BOTH processes, from the one file. That is the point.'));` : `
|
|
@@ -3296,13 +3329,19 @@ not cover. An agent that can sign could omit a field or send a different one, an
|
|
|
3296
3329
|
it would not fire. The gateway enforces scope and identity firmly; an input-dependent rule is only as
|
|
3297
3330
|
strong as whatever supplies its input.` : ''}
|
|
3298
3331
|
|
|
3299
|
-
**
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3332
|
+
**Also closed — replaying the same signed request.** \`harness-gateway.mjs\` remembers every nonce for as long as
|
|
3333
|
+
the guard would accept the request (about 5 minutes) and refuses the second copy (\`REPLAY_DETECTED\`). The memory is
|
|
3334
|
+
in-process: restarting the gateway forgets it.
|
|
3335
|
+
|
|
3336
|
+
${demo ? `**NOT closed:** anything that depends on history across many DIFFERENT calls (rate limits, circuit breakers,
|
|
3337
|
+
patterns). That needs a STATEFUL authority — the hosted gate's \`requireAuthorization\` atomically claims a real,
|
|
3338
|
+
single-use \`authorizationId\` against a database (see \`@metamynd/agentsafe-mcp-guard\`'s own README). A local
|
|
3339
|
+
harness has no database; that is the whole point of \`--harness\`. \`harness-gateway.mjs\` re-checks POLICY per
|
|
3340
|
+
request, which is real and worth having, but it does not remember what earlier calls did.` : `**NOT closed:** cumulative spend across many DIFFERENT calls. That needs a STATEFUL authority — the hosted gate's
|
|
3341
|
+
\`requireAuthorization\` atomically claims a real, single-use \`authorizationId\` against a database and holds the
|
|
3342
|
+
budget there (see \`@metamynd/agentsafe-mcp-guard\`'s own README). A local harness has no database; that is the
|
|
3343
|
+
whole point of \`--harness\`. \`harness-gateway.mjs\` re-checks POLICY per request, so each call is held to the
|
|
3344
|
+
per-transaction cap, but many calls under the cap are not added up here the way the hosted gate adds them up.`}
|
|
3306
3345
|
|
|
3307
3346
|
Also not closed by \`--gateway\` alone: cross-party trust (nobody but you can verify this agent's
|
|
3308
3347
|
identity or its decisions), evidence anyone but you can audit, a dashboard reachable when this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-metamynd-agent",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
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 direct-call, confused-deputy, replay, and cumulative-spend bypasses. --harness scaffolds a free, local, zero-network governance harness instead.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|