create-metamynd-agent 0.12.0 → 0.12.6
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 +8 -3
- package/index.mjs +74 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,9 +10,14 @@ default, a second `gateway/` process** built on
|
|
|
10
10
|
The agent's own `guardTool()` call is a fast, local, client-side check; the gateway is the real
|
|
11
11
|
enforcement boundary — see [Separate tool gateway](#separate-tool-gateway-default) below.
|
|
12
12
|
|
|
13
|
-
> **Prerequisite:** an agent is always owned by a person/org with a **MetaMynd account**.
|
|
14
|
-
>
|
|
15
|
-
>
|
|
13
|
+
> **Prerequisite:** an agent is always owned by a person/org with a **MetaMynd account**. The hosted
|
|
14
|
+
> flow provisions under your account's **verified principal** (KYC/KYB; on a beta deployment the
|
|
15
|
+
> platform can auto-approve one on first use) — if there is none, provisioning stops with "complete
|
|
16
|
+
> KYC/KYB first". That is all it needs: this CLI always provisions **Testnet** agents, so mainnet
|
|
17
|
+
> eligibility is not required. A mainnet agent is launched from the dashboard's **Launchpad**
|
|
18
|
+
> (`/dashboard/launchpad`) and needs a **mainnet-eligible** principal (verified by the identity
|
|
19
|
+
> provider, or reviewed by a person on the platform's team). `--harness` and `--sandbox` need no
|
|
20
|
+
> account or verification at all.
|
|
16
21
|
|
|
17
22
|
## Free local harness (no account, no network, `--harness`)
|
|
18
23
|
|
package/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// create-metamynd-agent — scaffold a MetaMynd/AgentSafe-governed agent in one command.
|
|
3
3
|
//
|
|
4
|
-
// Logs
|
|
4
|
+
// Logs the owner in (provisioning uses the owner's verified principal; the agent is always
|
|
5
|
+
// Testnet — mainnet goes through the dashboard Launchpad), provisions the agent in ONE call
|
|
5
6
|
// (POST /onboarding/agent → identity + mandate + starter SOP + enforced Standards),
|
|
6
7
|
// writes the portable `agent.metamynd.json` and a runnable agent example, PLUS (by default)
|
|
7
8
|
// a separate `gateway/` process — a second, independent guard that re-verifies every request
|
|
@@ -102,7 +103,13 @@ const MCP_GUARD_PKG = '@metamynd/agentsafe-mcp-guard';
|
|
|
102
103
|
// 0.6.0 brings buildAuthMessage's `resource` field and buildLocalDecisionMessage into this
|
|
103
104
|
// package's own bundled policy-core.mjs (alongside the guard's own 0.10.0) — no scaffolded
|
|
104
105
|
// template code changes, but the floor must still cover the real current version.
|
|
105
|
-
|
|
106
|
+
// 0.13.0: a daemon-held service key (keyProvider: 'daemon') now signs claims and settlements as the gateway's
|
|
107
|
+
// DID instead of claiming anonymously — refused on every mainnet hold before. No template change.
|
|
108
|
+
// 0.14.0: the claim can declare an x402 payment (x402: true) so the issuer observes lowered settlements. No template
|
|
109
|
+
// change (the scaffolded gateways don't pay by x402), but the floor must cover the real current version.
|
|
110
|
+
// 0.15.0: payloadDigestOf/toWireJson exported and refundAuthorization (its own signed `refund` action). No template
|
|
111
|
+
// change (the scaffolded gateways don't refund), but the floor must cover the real current version.
|
|
112
|
+
const MCP_GUARD_VERSION = '^0.15.0';
|
|
106
113
|
const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
107
114
|
// 0.2.0 fixes a confused-deputy gap (payload not bound to the signed request) — the CLI must
|
|
108
115
|
// never scaffold a range that could resolve below it.
|
|
@@ -114,7 +121,10 @@ const GATEWAY_PKG = '@metamynd/agentsafe-http-gateway';
|
|
|
114
121
|
// 0.5.0 adds the OPTIONAL Credential Vault `resolveCredential` hook on createHttpGateway (Module
|
|
115
122
|
// G) — additive and backward-compatible (every existing consumer sees zero behavior change), but
|
|
116
123
|
// the floor must still cover the real current version per this repo's own package-version check.
|
|
117
|
-
|
|
124
|
+
// 0.12.0: per-route `x402: true` declares an x402 payment on the claim. No template change.
|
|
125
|
+
// 0.13.0: a configured resolveCredential that refuses now FAILS CLOSED (502 CREDENTIAL_UNAVAILABLE) instead of forwarding
|
|
126
|
+
// without a credential. No template change (the scaffolded gateway does not use the Credential Vault).
|
|
127
|
+
const GATEWAY_VERSION = '^0.13.0';
|
|
118
128
|
const DEFAULT_API = 'https://metamynd.ai/api/v1';
|
|
119
129
|
const DEFAULT_GATEWAY_PORT = 4401; // distinct from --harness's dashboard (4400)
|
|
120
130
|
|
|
@@ -205,7 +215,7 @@ ${c.b('Options')}
|
|
|
205
215
|
--gateway --harness only: ALSO scaffold a second local process (still zero
|
|
206
216
|
network, zero account) that independently re-verifies every request
|
|
207
217
|
against the same rules file, using the real @metamynd/agentsafe-mcp-guard.
|
|
208
|
-
Off by default.
|
|
218
|
+
Off by default. Refuses a replayed request; does not track cumulative spend — see the
|
|
209
219
|
generated README#--gateway for exactly what it does and does not.
|
|
210
220
|
--gateway-port <n> The gateway process's port, hosted flow or --harness --gateway (default 4401)
|
|
211
221
|
--port <n> --harness only: the local dashboard's port (default 4400)
|
|
@@ -216,8 +226,16 @@ ${c.b('Options')}
|
|
|
216
226
|
${c.b('Environment')}
|
|
217
227
|
METAMYND_API, METAMYND_EMAIL, METAMYND_PASSWORD — fallbacks for the flags above
|
|
218
228
|
|
|
229
|
+
${c.b('Network')}
|
|
230
|
+
Every hosted agent this CLI provisions is a ${c.b('Testnet')} agent, whatever your verification status —
|
|
231
|
+
there is deliberately no mainnet flag. To launch a ${c.b('Mainnet')} agent, use the Launchpad in the
|
|
232
|
+
dashboard (https://metamynd.ai/dashboard/launchpad): its Mainnet option needs a mainnet-eligible
|
|
233
|
+
principal (verified by the identity provider, or reviewed by a person on the platform's team).
|
|
234
|
+
|
|
219
235
|
${c.b('What it does')}
|
|
220
|
-
1. Logs in as
|
|
236
|
+
1. Logs in as the owner → owner access token
|
|
237
|
+
(provisioning uses your account's verified principal — KYC/KYB, or one the platform
|
|
238
|
+
auto-approves on a beta deployment; mainnet eligibility is NOT needed for Testnet)
|
|
221
239
|
2. POST /onboarding/agent (one call) → identity + mandate + SOP + Standards
|
|
222
240
|
3. Writes agent.metamynd.json + index.mjs, PLUS (by default) a separate gateway/ process —
|
|
223
241
|
the real enforcement boundary, not index.mjs's own guard.guardTool() call. --no-gateway
|
|
@@ -2319,12 +2337,14 @@ function harnessRulesFile(mandate, sopDocument) {
|
|
|
2319
2337
|
// that skips its own guardToolLocal() call, or calls bookFlight() directly, gets nothing —
|
|
2320
2338
|
// the tool only runs in the gateway process now.
|
|
2321
2339
|
//
|
|
2340
|
+
// Replay of the SAME signed request is refused too (REPLAY_DETECTED): the gateway remembers each nonce for as long as
|
|
2341
|
+
// the guard would accept it, in memory (a restart forgets them). An independent tester's rerun (2026-09-24) found the
|
|
2342
|
+
// same bytes accepted twice before this.
|
|
2343
|
+
//
|
|
2322
2344
|
// 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.
|
|
2345
|
+
// cumulative spend across many DIFFERENT calls. That needs a STATEFUL authority — the hosted gate's
|
|
2346
|
+
// `requireAuthorization` claims a real, single-use authorizationId against a database and holds the budget there. A
|
|
2347
|
+
// local harness has no such database (that is the whole point of --harness). The README says so.
|
|
2328
2348
|
|
|
2329
2349
|
function harnessGatewayServerFile(scope, gatewayPort, agentDid, neutral = false) {
|
|
2330
2350
|
return `#!/usr/bin/env node
|
|
@@ -2415,9 +2435,35 @@ function readBody(req) {
|
|
|
2415
2435
|
});
|
|
2416
2436
|
}
|
|
2417
2437
|
|
|
2438
|
+
// Replay: every signed request carries a fresh nonce, and the guard refuses one more than 5 minutes old. So a nonce
|
|
2439
|
+
// only has to be remembered for that long to refuse the SAME signed request twice. Checked and recorded in one
|
|
2440
|
+
// synchronous step, before anything awaits, so two simultaneous copies cannot both get through. In memory: restarting
|
|
2441
|
+
// this process forgets it (a replay inside the 5-minute window after a restart is not caught). Cumulative spend across
|
|
2442
|
+
// many DIFFERENT requests is still not tracked here — that needs the hosted gate.
|
|
2443
|
+
const NONCE_TTL_MS = 6 * 60 * 1000;
|
|
2444
|
+
const seenNonces = new Map(); // agentDid|nonce -> expiry
|
|
2445
|
+
function firstUseOfNonce(headers) {
|
|
2446
|
+
let signed;
|
|
2447
|
+
try { signed = JSON.parse(headers['x-magp-request'] ?? 'null'); } catch { return true; } // malformed: the guard refuses it
|
|
2448
|
+
if (!signed || typeof signed.nonce !== 'string') return true; // no nonce: the guard refuses it
|
|
2449
|
+
const now = Date.now();
|
|
2450
|
+
if (seenNonces.size > 10000) for (const [k, exp] of seenNonces) if (exp <= now) seenNonces.delete(k);
|
|
2451
|
+
const key = String(signed.agentDid) + '|' + signed.nonce;
|
|
2452
|
+
const exp = seenNonces.get(key);
|
|
2453
|
+
if (exp !== undefined && exp > now) return false;
|
|
2454
|
+
seenNonces.set(key, now + NONCE_TTL_MS);
|
|
2455
|
+
return true;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
2418
2458
|
const server = http.createServer(async (req, res) => {
|
|
2419
2459
|
try {
|
|
2420
2460
|
const rawBody = await readBody(req);
|
|
2461
|
+
if (!firstUseOfNonce(req.headers)) {
|
|
2462
|
+
console.log('[harness-gateway] BLOCK ' + req.url + ' — REPLAY_DETECTED (this exact signed request already came through)');
|
|
2463
|
+
res.writeHead(403, { 'content-type': 'application/json', 'x-agentsafe-decision': 'block' });
|
|
2464
|
+
res.end(JSON.stringify({ decision: 'block', reasonCode: 'REPLAY_DETECTED' }));
|
|
2465
|
+
return;
|
|
2466
|
+
}
|
|
2421
2467
|
const result = await gateway({ method: req.method, path: req.url, headers: req.headers, rawBody });
|
|
2422
2468
|
const headers = { 'content-type': 'application/json' };
|
|
2423
2469
|
const decision = result.governance?.decision;
|
|
@@ -2978,9 +3024,9 @@ console.log(bold(' Checked twice, by two processes.') + ' bookFlight() lives in
|
|
|
2978
3024
|
console.log(dim(' not here. It independently re-verified every attempt above against the SAME'));
|
|
2979
3025
|
console.log(dim(' ./metamynd-rules.json, over a signed request, before running your tool.'));
|
|
2980
3026
|
console.log('');
|
|
2981
|
-
console.log(dim(' What --gateway does NOT close:
|
|
2982
|
-
console.log(dim(' calls.
|
|
2983
|
-
console.log(dim('
|
|
3027
|
+
console.log(dim(' It also refuses a replay of the same signed request. What --gateway does NOT close:'));
|
|
3028
|
+
console.log(dim(' cumulative spend across many different calls. That needs a STATEFUL authority (the'));
|
|
3029
|
+
console.log(dim(' hosted gate holds the budget in a database) - a local harness has none.'));
|
|
2984
3030
|
console.log(dim(' See README.md#--gateway for exactly what this does and does not prove.'));
|
|
2985
3031
|
console.log('');
|
|
2986
3032
|
console.log(' Edit ./metamynd-rules.json (or the dashboard) and run again - the outcome');
|
|
@@ -3175,9 +3221,8 @@ console.log(dim(' ./metamynd-rules.json, over a signed request, before running
|
|
|
3175
3221
|
console.log(dim(' It enforces scope and identity firmly; the rule inputs above are NOT signed, so an'));
|
|
3176
3222
|
console.log(dim(' agent that can sign could omit or forge one and its rule would not fire.'));
|
|
3177
3223
|
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.'));
|
|
3224
|
+
console.log(dim(' It also refuses a replay of the same signed request (remembered in memory, so a restart'));
|
|
3225
|
+
console.log(dim(' forgets). See README.md#--gateway for exactly what this does and does not prove.'));
|
|
3181
3226
|
console.log('');
|
|
3182
3227
|
console.log(' Edit ./metamynd-rules.json (or the dashboard) and run again - the outcome');
|
|
3183
3228
|
console.log(dim(' changes, in BOTH processes, from the one file. That is the point.'));` : `
|
|
@@ -3296,13 +3341,19 @@ not cover. An agent that can sign could omit a field or send a different one, an
|
|
|
3296
3341
|
it would not fire. The gateway enforces scope and identity firmly; an input-dependent rule is only as
|
|
3297
3342
|
strong as whatever supplies its input.` : ''}
|
|
3298
3343
|
|
|
3299
|
-
**
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3344
|
+
**Also closed — replaying the same signed request.** \`harness-gateway.mjs\` remembers every nonce for as long as
|
|
3345
|
+
the guard would accept the request (about 5 minutes) and refuses the second copy (\`REPLAY_DETECTED\`). The memory is
|
|
3346
|
+
in-process: restarting the gateway forgets it.
|
|
3347
|
+
|
|
3348
|
+
${demo ? `**NOT closed:** anything that depends on history across many DIFFERENT calls (rate limits, circuit breakers,
|
|
3349
|
+
patterns). That needs a STATEFUL authority — the hosted gate's \`requireAuthorization\` atomically claims a real,
|
|
3350
|
+
single-use \`authorizationId\` against a database (see \`@metamynd/agentsafe-mcp-guard\`'s own README). A local
|
|
3351
|
+
harness has no database; that is the whole point of \`--harness\`. \`harness-gateway.mjs\` re-checks POLICY per
|
|
3352
|
+
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
|
|
3353
|
+
\`requireAuthorization\` atomically claims a real, single-use \`authorizationId\` against a database and holds the
|
|
3354
|
+
budget there (see \`@metamynd/agentsafe-mcp-guard\`'s own README). A local harness has no database; that is the
|
|
3355
|
+
whole point of \`--harness\`. \`harness-gateway.mjs\` re-checks POLICY per request, so each call is held to the
|
|
3356
|
+
per-transaction cap, but many calls under the cap are not added up here the way the hosted gate adds them up.`}
|
|
3306
3357
|
|
|
3307
3358
|
Also not closed by \`--gateway\` alone: cross-party trust (nobody but you can verify this agent's
|
|
3308
3359
|
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.6",
|
|
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": {
|