@metamynd/agentsafe-guard 0.2.0 → 0.3.0
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/LICENSE +21 -21
- package/README.md +303 -279
- package/agentsafe-guard.mjs +606 -480
- package/example-openclaw-agent.mjs +47 -47
- package/magp-did.mjs +157 -157
- package/package.json +59 -59
- package/policy-core.mjs +83 -4
- package/x402.mjs +43 -43
|
@@ -1,47 +1,47 @@
|
|
|
1
|
-
// example-openclaw-agent.mjs — a runnable demo of the guard against a seeded bound agent.
|
|
2
|
-
//
|
|
3
|
-
// Simulates how an OpenClaw agent's TOOL is wrapped: the raw tool only runs if the
|
|
4
|
-
// AgentSafe gate returns `allow`. Run it after seeding an agent with
|
|
5
|
-
// backend/scripts/demo-seed-governance.ts:
|
|
6
|
-
//
|
|
7
|
-
// $env:AGENTSAFE_API="http://localhost:9926/api/v1"
|
|
8
|
-
// $env:AGENT_DID="did:hedera:testnet:..."
|
|
9
|
-
// $env:AGENT_KEY="302e0201..."
|
|
10
|
-
// node example-openclaw-agent.mjs
|
|
11
|
-
import { createGuard } from './agentsafe-guard.mjs';
|
|
12
|
-
|
|
13
|
-
const guard = createGuard({
|
|
14
|
-
api: process.env.AGENTSAFE_API ?? 'http://localhost:9926/api/v1',
|
|
15
|
-
agentDid: process.env.AGENT_DID,
|
|
16
|
-
agentKey: process.env.AGENT_KEY,
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
// --- The agent's tool. In OpenClaw you register this handler for the tool; here we
|
|
20
|
-
// wrap it with guard.guardTool so every call is gated first. ---
|
|
21
|
-
const bookFlight = guard.guardTool(
|
|
22
|
-
'flight-purchase', // the governed action (matches the mandate scope)
|
|
23
|
-
async (args, decision) => {
|
|
24
|
-
// Only reached when the gate ALLOWED. Real booking would go here.
|
|
25
|
-
return { booked: true, pnr: 'PNR-DEMO', remaining: decision.remaining, ...args };
|
|
26
|
-
},
|
|
27
|
-
// Map the tool args → gate inputs. `context` carries what the Standard/SOP rules need.
|
|
28
|
-
(a) => ({ amount: a.amount, currency: 'USD', merchant: a.merchant, context: { tool: a.tool ?? 'book-flight', riskLevel: a.riskLevel ?? 'low' } }),
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
async function ask(label, args) {
|
|
32
|
-
try {
|
|
33
|
-
const r = await bookFlight(args);
|
|
34
|
-
console.log(` \x1b[32m✅ ALLOW\x1b[0m ${label.padEnd(30)} → booked ${r.pnr} (remaining $${r.remaining})`);
|
|
35
|
-
} catch (e) {
|
|
36
|
-
const g = e.governance ?? {};
|
|
37
|
-
const tag = g.decision === 'escalate' ? '\x1b[33m⚠ ESCALATE\x1b[0m' : '\x1b[31m⛔ BLOCK\x1b[0m';
|
|
38
|
-
console.log(` ${tag} ${label.padEnd(30)} → ${g.reasonCode ?? e.message}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
console.log(`\n OpenClaw agent — every booking passes through the AgentSafe gate\n ${'─'.repeat(60)}`);
|
|
43
|
-
await ask('$150 book-flight, low risk', { amount: 150, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'low' });
|
|
44
|
-
await ask('$600 book-flight', { amount: 600, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'low' });
|
|
45
|
-
await ask('$100 wire-transfer tool', { amount: 100, merchant: 'skyward-air', tool: 'wire-transfer', riskLevel: 'low' });
|
|
46
|
-
await ask('$100 high-risk decision', { amount: 100, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'high' });
|
|
47
|
-
console.log(`\n The agent refuses blocked/escalated actions itself — governance decided, not the LLM.\n`);
|
|
1
|
+
// example-openclaw-agent.mjs — a runnable demo of the guard against a seeded bound agent.
|
|
2
|
+
//
|
|
3
|
+
// Simulates how an OpenClaw agent's TOOL is wrapped: the raw tool only runs if the
|
|
4
|
+
// AgentSafe gate returns `allow`. Run it after seeding an agent with
|
|
5
|
+
// backend/scripts/demo-seed-governance.ts:
|
|
6
|
+
//
|
|
7
|
+
// $env:AGENTSAFE_API="http://localhost:9926/api/v1"
|
|
8
|
+
// $env:AGENT_DID="did:hedera:testnet:..."
|
|
9
|
+
// $env:AGENT_KEY="302e0201..."
|
|
10
|
+
// node example-openclaw-agent.mjs
|
|
11
|
+
import { createGuard } from './agentsafe-guard.mjs';
|
|
12
|
+
|
|
13
|
+
const guard = createGuard({
|
|
14
|
+
api: process.env.AGENTSAFE_API ?? 'http://localhost:9926/api/v1',
|
|
15
|
+
agentDid: process.env.AGENT_DID,
|
|
16
|
+
agentKey: process.env.AGENT_KEY,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// --- The agent's tool. In OpenClaw you register this handler for the tool; here we
|
|
20
|
+
// wrap it with guard.guardTool so every call is gated first. ---
|
|
21
|
+
const bookFlight = guard.guardTool(
|
|
22
|
+
'flight-purchase', // the governed action (matches the mandate scope)
|
|
23
|
+
async (args, decision) => {
|
|
24
|
+
// Only reached when the gate ALLOWED. Real booking would go here.
|
|
25
|
+
return { booked: true, pnr: 'PNR-DEMO', remaining: decision.remaining, ...args };
|
|
26
|
+
},
|
|
27
|
+
// Map the tool args → gate inputs. `context` carries what the Standard/SOP rules need.
|
|
28
|
+
(a) => ({ amount: a.amount, currency: 'USD', merchant: a.merchant, context: { tool: a.tool ?? 'book-flight', riskLevel: a.riskLevel ?? 'low' } }),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
async function ask(label, args) {
|
|
32
|
+
try {
|
|
33
|
+
const r = await bookFlight(args);
|
|
34
|
+
console.log(` \x1b[32m✅ ALLOW\x1b[0m ${label.padEnd(30)} → booked ${r.pnr} (remaining $${r.remaining})`);
|
|
35
|
+
} catch (e) {
|
|
36
|
+
const g = e.governance ?? {};
|
|
37
|
+
const tag = g.decision === 'escalate' ? '\x1b[33m⚠ ESCALATE\x1b[0m' : '\x1b[31m⛔ BLOCK\x1b[0m';
|
|
38
|
+
console.log(` ${tag} ${label.padEnd(30)} → ${g.reasonCode ?? e.message}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log(`\n OpenClaw agent — every booking passes through the AgentSafe gate\n ${'─'.repeat(60)}`);
|
|
43
|
+
await ask('$150 book-flight, low risk', { amount: 150, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'low' });
|
|
44
|
+
await ask('$600 book-flight', { amount: 600, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'low' });
|
|
45
|
+
await ask('$100 wire-transfer tool', { amount: 100, merchant: 'skyward-air', tool: 'wire-transfer', riskLevel: 'low' });
|
|
46
|
+
await ask('$100 high-risk decision', { amount: 100, merchant: 'skyward-air', tool: 'book-flight', riskLevel: 'high' });
|
|
47
|
+
console.log(`\n The agent refuses blocked/escalated actions itself — governance decided, not the LLM.\n`);
|
package/magp-did.mjs
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
// GENERATED from backend/src/features/magp/did.ts — do not edit. Regenerate: npm run build:guard-core
|
|
2
|
-
|
|
3
|
-
// src/features/magp/did.ts
|
|
4
|
-
import crypto from "node:crypto";
|
|
5
|
-
|
|
6
|
-
// src/features/agent-identity/did.util.ts
|
|
7
|
-
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
8
|
-
function base58(bytes) {
|
|
9
|
-
let zeros = 0;
|
|
10
|
-
while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
|
|
11
|
-
const digits = [];
|
|
12
|
-
for (let i = zeros; i < bytes.length; i++) {
|
|
13
|
-
let carry = bytes[i];
|
|
14
|
-
for (let j = 0; j < digits.length; j++) {
|
|
15
|
-
carry += digits[j] << 8;
|
|
16
|
-
digits[j] = carry % 58;
|
|
17
|
-
carry = carry / 58 | 0;
|
|
18
|
-
}
|
|
19
|
-
while (carry > 0) {
|
|
20
|
-
digits.push(carry % 58);
|
|
21
|
-
carry = carry / 58 | 0;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
let out = "";
|
|
25
|
-
for (let k = 0; k < zeros; k++) out += BASE58_ALPHABET[0];
|
|
26
|
-
for (let q = digits.length - 1; q >= 0; q--) out += BASE58_ALPHABET[digits[q]];
|
|
27
|
-
return out;
|
|
28
|
-
}
|
|
29
|
-
function multibaseBase58btc(bytes) {
|
|
30
|
-
return "z" + base58(bytes);
|
|
31
|
-
}
|
|
32
|
-
function buildHederaDid(network, publicKeyBytes, topicId) {
|
|
33
|
-
return `did:hedera:${network}:${multibaseBase58btc(publicKeyBytes)}_${topicId}`;
|
|
34
|
-
}
|
|
35
|
-
function base58Decode(str) {
|
|
36
|
-
const bytes = [0];
|
|
37
|
-
for (const ch of str) {
|
|
38
|
-
const value = BASE58_ALPHABET.indexOf(ch);
|
|
39
|
-
if (value === -1) throw new Error(`invalid base58 character '${ch}'`);
|
|
40
|
-
let carry = value;
|
|
41
|
-
for (let j = 0; j < bytes.length; j++) {
|
|
42
|
-
carry += bytes[j] * 58;
|
|
43
|
-
bytes[j] = carry & 255;
|
|
44
|
-
carry >>= 8;
|
|
45
|
-
}
|
|
46
|
-
while (carry > 0) {
|
|
47
|
-
bytes.push(carry & 255);
|
|
48
|
-
carry >>= 8;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
let zeros = 0;
|
|
52
|
-
for (let k = 0; k < str.length && str[k] === BASE58_ALPHABET[0]; k++) zeros++;
|
|
53
|
-
const out = new Uint8Array(zeros + bytes.length);
|
|
54
|
-
for (let i = 0; i < bytes.length; i++) out[zeros + i] = bytes[bytes.length - 1 - i];
|
|
55
|
-
return out;
|
|
56
|
-
}
|
|
57
|
-
function parseHederaDid(did) {
|
|
58
|
-
const m = /^did:hedera:(mainnet|testnet|previewnet|devnet):(z[1-9A-HJ-NP-Za-km-z]+)_(\d+\.\d+\.\d+)$/.exec(did ?? "");
|
|
59
|
-
if (!m) return null;
|
|
60
|
-
const [, network, publicKeyMultibase, topicId] = m;
|
|
61
|
-
let publicKeyBytes;
|
|
62
|
-
try {
|
|
63
|
-
publicKeyBytes = base58Decode(publicKeyMultibase.slice(1));
|
|
64
|
-
} catch {
|
|
65
|
-
return null;
|
|
66
|
-
}
|
|
67
|
-
if (publicKeyBytes.length !== 32) return null;
|
|
68
|
-
return { network, publicKeyMultibase, publicKeyBytes, topicId };
|
|
69
|
-
}
|
|
70
|
-
var ED25519_MULTICODEC = Uint8Array.of(237, 1);
|
|
71
|
-
function buildDidKey(publicKeyBytes) {
|
|
72
|
-
const prefixed = new Uint8Array(ED25519_MULTICODEC.length + publicKeyBytes.length);
|
|
73
|
-
prefixed.set(ED25519_MULTICODEC, 0);
|
|
74
|
-
prefixed.set(publicKeyBytes, ED25519_MULTICODEC.length);
|
|
75
|
-
return `did:key:${multibaseBase58btc(prefixed)}`;
|
|
76
|
-
}
|
|
77
|
-
function parseDidKey(did) {
|
|
78
|
-
const m = /^did:key:(z[1-9A-HJ-NP-Za-km-z]+)$/.exec(did ?? "");
|
|
79
|
-
if (!m) return null;
|
|
80
|
-
const multibase = m[1];
|
|
81
|
-
let decoded;
|
|
82
|
-
try {
|
|
83
|
-
decoded = base58Decode(multibase.slice(1));
|
|
84
|
-
} catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
if (decoded.length !== ED25519_MULTICODEC.length + 32) return null;
|
|
88
|
-
if (decoded[0] !== ED25519_MULTICODEC[0] || decoded[1] !== ED25519_MULTICODEC[1]) return null;
|
|
89
|
-
return { method: "key", publicKeyMultibase: multibase, publicKeyBytes: decoded.slice(ED25519_MULTICODEC.length) };
|
|
90
|
-
}
|
|
91
|
-
function didPublicKeyBytes(did) {
|
|
92
|
-
return parseHederaDid(did)?.publicKeyBytes ?? parseDidKey(did)?.publicKeyBytes ?? null;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// src/features/magp/did.ts
|
|
96
|
-
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
97
|
-
function ed25519KeyFromRaw(raw) {
|
|
98
|
-
const der = Buffer.concat([ED25519_SPKI_PREFIX, Buffer.from(raw)]);
|
|
99
|
-
return crypto.createPublicKey({ key: der, format: "der", type: "spki" });
|
|
100
|
-
}
|
|
101
|
-
function verifyDidSignature(did, message, signatureHex) {
|
|
102
|
-
const publicKeyBytes = didPublicKeyBytes(did);
|
|
103
|
-
if (!publicKeyBytes) return false;
|
|
104
|
-
try {
|
|
105
|
-
const key = ed25519KeyFromRaw(publicKeyBytes);
|
|
106
|
-
return crypto.verify(null, Buffer.from(message, "utf8"), key, Buffer.from(signatureHex, "hex"));
|
|
107
|
-
} catch {
|
|
108
|
-
return false;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function buildDidDocument(did, service) {
|
|
112
|
-
const hedera = parseHederaDid(did);
|
|
113
|
-
const key = hedera ? null : parseDidKey(did);
|
|
114
|
-
if (!hedera && !key) return null;
|
|
115
|
-
const publicKeyMultibase = hedera ? hedera.publicKeyMultibase : key.publicKeyMultibase;
|
|
116
|
-
const fragment = hedera ? "#did-root-key" : `#${key.publicKeyMultibase}`;
|
|
117
|
-
const vmId = `${did}${fragment}`;
|
|
118
|
-
const doc = {
|
|
119
|
-
"@context": ["https://www.w3.org/ns/did/v1"],
|
|
120
|
-
id: did,
|
|
121
|
-
controller: did,
|
|
122
|
-
verificationMethod: [
|
|
123
|
-
{
|
|
124
|
-
id: vmId,
|
|
125
|
-
type: "Ed25519VerificationKey2020",
|
|
126
|
-
controller: did,
|
|
127
|
-
publicKeyMultibase
|
|
128
|
-
}
|
|
129
|
-
],
|
|
130
|
-
authentication: [vmId]
|
|
131
|
-
};
|
|
132
|
-
if (service) {
|
|
133
|
-
doc.service = [
|
|
134
|
-
{
|
|
135
|
-
id: `${did}#magp`,
|
|
136
|
-
type: "MAGPEndpoint",
|
|
137
|
-
serviceEndpoint: service.serviceEndpoint,
|
|
138
|
-
channels: service.channels,
|
|
139
|
-
protoVersions: service.protoVersions
|
|
140
|
-
}
|
|
141
|
-
];
|
|
142
|
-
}
|
|
143
|
-
return doc;
|
|
144
|
-
}
|
|
145
|
-
export {
|
|
146
|
-
base58,
|
|
147
|
-
base58Decode,
|
|
148
|
-
buildDidDocument,
|
|
149
|
-
buildDidKey,
|
|
150
|
-
buildHederaDid,
|
|
151
|
-
didPublicKeyBytes,
|
|
152
|
-
ed25519KeyFromRaw,
|
|
153
|
-
multibaseBase58btc,
|
|
154
|
-
parseDidKey,
|
|
155
|
-
parseHederaDid,
|
|
156
|
-
verifyDidSignature
|
|
157
|
-
};
|
|
1
|
+
// GENERATED from backend/src/features/magp/did.ts — do not edit. Regenerate: npm run build:guard-core
|
|
2
|
+
|
|
3
|
+
// src/features/magp/did.ts
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
|
|
6
|
+
// src/features/agent-identity/did.util.ts
|
|
7
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
8
|
+
function base58(bytes) {
|
|
9
|
+
let zeros = 0;
|
|
10
|
+
while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
|
|
11
|
+
const digits = [];
|
|
12
|
+
for (let i = zeros; i < bytes.length; i++) {
|
|
13
|
+
let carry = bytes[i];
|
|
14
|
+
for (let j = 0; j < digits.length; j++) {
|
|
15
|
+
carry += digits[j] << 8;
|
|
16
|
+
digits[j] = carry % 58;
|
|
17
|
+
carry = carry / 58 | 0;
|
|
18
|
+
}
|
|
19
|
+
while (carry > 0) {
|
|
20
|
+
digits.push(carry % 58);
|
|
21
|
+
carry = carry / 58 | 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
let out = "";
|
|
25
|
+
for (let k = 0; k < zeros; k++) out += BASE58_ALPHABET[0];
|
|
26
|
+
for (let q = digits.length - 1; q >= 0; q--) out += BASE58_ALPHABET[digits[q]];
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function multibaseBase58btc(bytes) {
|
|
30
|
+
return "z" + base58(bytes);
|
|
31
|
+
}
|
|
32
|
+
function buildHederaDid(network, publicKeyBytes, topicId) {
|
|
33
|
+
return `did:hedera:${network}:${multibaseBase58btc(publicKeyBytes)}_${topicId}`;
|
|
34
|
+
}
|
|
35
|
+
function base58Decode(str) {
|
|
36
|
+
const bytes = [0];
|
|
37
|
+
for (const ch of str) {
|
|
38
|
+
const value = BASE58_ALPHABET.indexOf(ch);
|
|
39
|
+
if (value === -1) throw new Error(`invalid base58 character '${ch}'`);
|
|
40
|
+
let carry = value;
|
|
41
|
+
for (let j = 0; j < bytes.length; j++) {
|
|
42
|
+
carry += bytes[j] * 58;
|
|
43
|
+
bytes[j] = carry & 255;
|
|
44
|
+
carry >>= 8;
|
|
45
|
+
}
|
|
46
|
+
while (carry > 0) {
|
|
47
|
+
bytes.push(carry & 255);
|
|
48
|
+
carry >>= 8;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
let zeros = 0;
|
|
52
|
+
for (let k = 0; k < str.length && str[k] === BASE58_ALPHABET[0]; k++) zeros++;
|
|
53
|
+
const out = new Uint8Array(zeros + bytes.length);
|
|
54
|
+
for (let i = 0; i < bytes.length; i++) out[zeros + i] = bytes[bytes.length - 1 - i];
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
function parseHederaDid(did) {
|
|
58
|
+
const m = /^did:hedera:(mainnet|testnet|previewnet|devnet):(z[1-9A-HJ-NP-Za-km-z]+)_(\d+\.\d+\.\d+)$/.exec(did ?? "");
|
|
59
|
+
if (!m) return null;
|
|
60
|
+
const [, network, publicKeyMultibase, topicId] = m;
|
|
61
|
+
let publicKeyBytes;
|
|
62
|
+
try {
|
|
63
|
+
publicKeyBytes = base58Decode(publicKeyMultibase.slice(1));
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (publicKeyBytes.length !== 32) return null;
|
|
68
|
+
return { network, publicKeyMultibase, publicKeyBytes, topicId };
|
|
69
|
+
}
|
|
70
|
+
var ED25519_MULTICODEC = Uint8Array.of(237, 1);
|
|
71
|
+
function buildDidKey(publicKeyBytes) {
|
|
72
|
+
const prefixed = new Uint8Array(ED25519_MULTICODEC.length + publicKeyBytes.length);
|
|
73
|
+
prefixed.set(ED25519_MULTICODEC, 0);
|
|
74
|
+
prefixed.set(publicKeyBytes, ED25519_MULTICODEC.length);
|
|
75
|
+
return `did:key:${multibaseBase58btc(prefixed)}`;
|
|
76
|
+
}
|
|
77
|
+
function parseDidKey(did) {
|
|
78
|
+
const m = /^did:key:(z[1-9A-HJ-NP-Za-km-z]+)$/.exec(did ?? "");
|
|
79
|
+
if (!m) return null;
|
|
80
|
+
const multibase = m[1];
|
|
81
|
+
let decoded;
|
|
82
|
+
try {
|
|
83
|
+
decoded = base58Decode(multibase.slice(1));
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (decoded.length !== ED25519_MULTICODEC.length + 32) return null;
|
|
88
|
+
if (decoded[0] !== ED25519_MULTICODEC[0] || decoded[1] !== ED25519_MULTICODEC[1]) return null;
|
|
89
|
+
return { method: "key", publicKeyMultibase: multibase, publicKeyBytes: decoded.slice(ED25519_MULTICODEC.length) };
|
|
90
|
+
}
|
|
91
|
+
function didPublicKeyBytes(did) {
|
|
92
|
+
return parseHederaDid(did)?.publicKeyBytes ?? parseDidKey(did)?.publicKeyBytes ?? null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/features/magp/did.ts
|
|
96
|
+
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
97
|
+
function ed25519KeyFromRaw(raw) {
|
|
98
|
+
const der = Buffer.concat([ED25519_SPKI_PREFIX, Buffer.from(raw)]);
|
|
99
|
+
return crypto.createPublicKey({ key: der, format: "der", type: "spki" });
|
|
100
|
+
}
|
|
101
|
+
function verifyDidSignature(did, message, signatureHex) {
|
|
102
|
+
const publicKeyBytes = didPublicKeyBytes(did);
|
|
103
|
+
if (!publicKeyBytes) return false;
|
|
104
|
+
try {
|
|
105
|
+
const key = ed25519KeyFromRaw(publicKeyBytes);
|
|
106
|
+
return crypto.verify(null, Buffer.from(message, "utf8"), key, Buffer.from(signatureHex, "hex"));
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function buildDidDocument(did, service) {
|
|
112
|
+
const hedera = parseHederaDid(did);
|
|
113
|
+
const key = hedera ? null : parseDidKey(did);
|
|
114
|
+
if (!hedera && !key) return null;
|
|
115
|
+
const publicKeyMultibase = hedera ? hedera.publicKeyMultibase : key.publicKeyMultibase;
|
|
116
|
+
const fragment = hedera ? "#did-root-key" : `#${key.publicKeyMultibase}`;
|
|
117
|
+
const vmId = `${did}${fragment}`;
|
|
118
|
+
const doc = {
|
|
119
|
+
"@context": ["https://www.w3.org/ns/did/v1"],
|
|
120
|
+
id: did,
|
|
121
|
+
controller: did,
|
|
122
|
+
verificationMethod: [
|
|
123
|
+
{
|
|
124
|
+
id: vmId,
|
|
125
|
+
type: "Ed25519VerificationKey2020",
|
|
126
|
+
controller: did,
|
|
127
|
+
publicKeyMultibase
|
|
128
|
+
}
|
|
129
|
+
],
|
|
130
|
+
authentication: [vmId]
|
|
131
|
+
};
|
|
132
|
+
if (service) {
|
|
133
|
+
doc.service = [
|
|
134
|
+
{
|
|
135
|
+
id: `${did}#magp`,
|
|
136
|
+
type: "MAGPEndpoint",
|
|
137
|
+
serviceEndpoint: service.serviceEndpoint,
|
|
138
|
+
channels: service.channels,
|
|
139
|
+
protoVersions: service.protoVersions
|
|
140
|
+
}
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
return doc;
|
|
144
|
+
}
|
|
145
|
+
export {
|
|
146
|
+
base58,
|
|
147
|
+
base58Decode,
|
|
148
|
+
buildDidDocument,
|
|
149
|
+
buildDidKey,
|
|
150
|
+
buildHederaDid,
|
|
151
|
+
didPublicKeyBytes,
|
|
152
|
+
ed25519KeyFromRaw,
|
|
153
|
+
multibaseBase58btc,
|
|
154
|
+
parseDidKey,
|
|
155
|
+
parseHederaDid,
|
|
156
|
+
verifyDidSignature
|
|
157
|
+
};
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@metamynd/agentsafe-guard",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Zero-dependency runtime governance for any Node AI agent — gate tool calls through MetaMynd/AgentSafe (allow / block / escalate) against the agent's mandate, enforced Standards, and SOPs. Ed25519-signed, deterministic, fail-closed.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./agentsafe-guard.mjs",
|
|
7
|
-
"module": "./agentsafe-guard.mjs",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": "./agentsafe-guard.mjs",
|
|
10
|
-
"./policy-core": "./policy-core.mjs",
|
|
11
|
-
"./package.json": "./package.json"
|
|
12
|
-
},
|
|
13
|
-
"files": [
|
|
14
|
-
"agentsafe-guard.mjs",
|
|
15
|
-
"policy-core.mjs",
|
|
16
|
-
"magp-did.mjs",
|
|
17
|
-
"x402.mjs",
|
|
18
|
-
"example-openclaw-agent.mjs",
|
|
19
|
-
"README.md",
|
|
20
|
-
"LICENSE"
|
|
21
|
-
],
|
|
22
|
-
"scripts": {
|
|
23
|
-
"test": "node local-eval.smoke.mjs"
|
|
24
|
-
},
|
|
25
|
-
"engines": {
|
|
26
|
-
"node": ">=18"
|
|
27
|
-
},
|
|
28
|
-
"sideEffects": false,
|
|
29
|
-
"keywords": [
|
|
30
|
-
"ai",
|
|
31
|
-
"agent",
|
|
32
|
-
"agents",
|
|
33
|
-
"governance",
|
|
34
|
-
"ai-safety",
|
|
35
|
-
"guardrails",
|
|
36
|
-
"policy",
|
|
37
|
-
"mandate",
|
|
38
|
-
"authorization",
|
|
39
|
-
"ed25519",
|
|
40
|
-
"magp",
|
|
41
|
-
"metamynd",
|
|
42
|
-
"agentsafe",
|
|
43
|
-
"compliance"
|
|
44
|
-
],
|
|
45
|
-
"author": "MetaMynd",
|
|
46
|
-
"license": "MIT",
|
|
47
|
-
"homepage": "https://github.com/jasimp18/AgentSafe/tree/main/integrations/agentsafe-guard#readme",
|
|
48
|
-
"repository": {
|
|
49
|
-
"type": "git",
|
|
50
|
-
"url": "git+https://github.com/jasimp18/AgentSafe.git",
|
|
51
|
-
"directory": "integrations/agentsafe-guard"
|
|
52
|
-
},
|
|
53
|
-
"bugs": {
|
|
54
|
-
"url": "https://github.com/jasimp18/AgentSafe/issues"
|
|
55
|
-
},
|
|
56
|
-
"publishConfig": {
|
|
57
|
-
"access": "public"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamynd/agentsafe-guard",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Zero-dependency runtime governance for any Node AI agent — gate tool calls through MetaMynd/AgentSafe (allow / block / escalate) against the agent's mandate, enforced Standards, and SOPs. Ed25519-signed, deterministic, fail-closed.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./agentsafe-guard.mjs",
|
|
7
|
+
"module": "./agentsafe-guard.mjs",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": "./agentsafe-guard.mjs",
|
|
10
|
+
"./policy-core": "./policy-core.mjs",
|
|
11
|
+
"./package.json": "./package.json"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"agentsafe-guard.mjs",
|
|
15
|
+
"policy-core.mjs",
|
|
16
|
+
"magp-did.mjs",
|
|
17
|
+
"x402.mjs",
|
|
18
|
+
"example-openclaw-agent.mjs",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"test": "node local-eval.smoke.mjs && node execution-adapter.smoke.mjs"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"keywords": [
|
|
30
|
+
"ai",
|
|
31
|
+
"agent",
|
|
32
|
+
"agents",
|
|
33
|
+
"governance",
|
|
34
|
+
"ai-safety",
|
|
35
|
+
"guardrails",
|
|
36
|
+
"policy",
|
|
37
|
+
"mandate",
|
|
38
|
+
"authorization",
|
|
39
|
+
"ed25519",
|
|
40
|
+
"magp",
|
|
41
|
+
"metamynd",
|
|
42
|
+
"agentsafe",
|
|
43
|
+
"compliance"
|
|
44
|
+
],
|
|
45
|
+
"author": "MetaMynd",
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"homepage": "https://github.com/jasimp18/AgentSafe/tree/main/integrations/agentsafe-guard#readme",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/jasimp18/AgentSafe.git",
|
|
51
|
+
"directory": "integrations/agentsafe-guard"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/jasimp18/AgentSafe/issues"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
}
|
|
59
|
+
}
|