triage-ows 1.0.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/README.md +786 -0
- package/bin/triage-ows.js +218 -0
- package/bin/triage-policy.js +66 -0
- package/bin/triage-server.js +4 -0
- package/dashboard-dist/assets/index-Dnhi_dJQ.css +2 -0
- package/dashboard-dist/assets/index-g_2MwC-o.js +9 -0
- package/dashboard-dist/favicon.svg +7 -0
- package/dashboard-dist/index.html +16 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/emitter.d.ts +7 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +41 -0
- package/dist/emitter.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/index.js.map +1 -0
- package/dist/scoring/behavior.d.ts +3 -0
- package/dist/scoring/behavior.d.ts.map +1 -0
- package/dist/scoring/behavior.js +13 -0
- package/dist/scoring/behavior.js.map +1 -0
- package/dist/scoring/compliance.d.ts +3 -0
- package/dist/scoring/compliance.d.ts.map +1 -0
- package/dist/scoring/compliance.js +11 -0
- package/dist/scoring/compliance.js.map +1 -0
- package/dist/scoring/identity.d.ts +3 -0
- package/dist/scoring/identity.d.ts.map +1 -0
- package/dist/scoring/identity.js +16 -0
- package/dist/scoring/identity.js.map +1 -0
- package/dist/scoring/index.d.ts +10 -0
- package/dist/scoring/index.d.ts.map +1 -0
- package/dist/scoring/index.js +35 -0
- package/dist/scoring/index.js.map +1 -0
- package/dist/scoring/limits.d.ts +7 -0
- package/dist/scoring/limits.d.ts.map +1 -0
- package/dist/scoring/limits.js +9 -0
- package/dist/scoring/limits.js.map +1 -0
- package/dist/scoring/network.d.ts +3 -0
- package/dist/scoring/network.d.ts.map +1 -0
- package/dist/scoring/network.js +16 -0
- package/dist/scoring/network.js.map +1 -0
- package/dist/scoring/onchain.d.ts +4 -0
- package/dist/scoring/onchain.d.ts.map +1 -0
- package/dist/scoring/onchain.js +35 -0
- package/dist/scoring/onchain.js.map +1 -0
- package/dist/scoring/risk.d.ts +3 -0
- package/dist/scoring/risk.d.ts.map +1 -0
- package/dist/scoring/risk.js +22 -0
- package/dist/scoring/risk.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +405 -0
- package/dist/server.js.map +1 -0
- package/dist/store.d.ts +13 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +177 -0
- package/dist/store.js.map +1 -0
- package/dist/types.d.ts +107 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/dist/webbotauth.d.ts +38 -0
- package/dist/webbotauth.d.ts.map +1 -0
- package/dist/webbotauth.js +120 -0
- package/dist/webbotauth.js.map +1 -0
- package/dist/xmtp.d.ts +6 -0
- package/dist/xmtp.d.ts.map +1 -0
- package/dist/xmtp.js +161 -0
- package/dist/xmtp.js.map +1 -0
- package/package.json +58 -0
- package/policy-template.json +14 -0
- package/src/config.ts +86 -0
- package/src/emitter.ts +40 -0
- package/src/index.ts +18 -0
- package/src/scoring/behavior.ts +15 -0
- package/src/scoring/compliance.ts +12 -0
- package/src/scoring/identity.ts +12 -0
- package/src/scoring/index.ts +31 -0
- package/src/scoring/limits.ts +10 -0
- package/src/scoring/network.ts +18 -0
- package/src/scoring/onchain.ts +44 -0
- package/src/scoring/risk.ts +25 -0
- package/src/server.ts +410 -0
- package/src/store.ts +197 -0
- package/src/types.ts +137 -0
- package/src/webbotauth.ts +128 -0
- package/src/xmtp.ts +188 -0
- package/triage.config.example.json +22 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
export interface PolicyContext {
|
|
2
|
+
chain_id: string;
|
|
3
|
+
wallet_id: string;
|
|
4
|
+
api_key_id: string;
|
|
5
|
+
transaction: {
|
|
6
|
+
to: string;
|
|
7
|
+
value: string;
|
|
8
|
+
raw_hex: string;
|
|
9
|
+
data: string;
|
|
10
|
+
};
|
|
11
|
+
spending: {
|
|
12
|
+
daily_total: string;
|
|
13
|
+
date: string;
|
|
14
|
+
};
|
|
15
|
+
timestamp: string;
|
|
16
|
+
policy_config?: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
export interface PolicyResult {
|
|
19
|
+
allow: boolean;
|
|
20
|
+
reason?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface TrustBreakdown {
|
|
23
|
+
identity: number;
|
|
24
|
+
onChain: number;
|
|
25
|
+
behavior: number;
|
|
26
|
+
compliance: number;
|
|
27
|
+
network: number;
|
|
28
|
+
risk: number;
|
|
29
|
+
total: number;
|
|
30
|
+
}
|
|
31
|
+
export interface SpendingTier {
|
|
32
|
+
name: 'Sovereign' | 'Trusted' | 'Building' | 'Cautious' | 'Restricted' | 'Frozen';
|
|
33
|
+
color: string;
|
|
34
|
+
dailyLimit: number;
|
|
35
|
+
perTxLimit: number;
|
|
36
|
+
minScore: number;
|
|
37
|
+
}
|
|
38
|
+
export interface AgentProfile {
|
|
39
|
+
address: string;
|
|
40
|
+
walletId?: string;
|
|
41
|
+
apiKeyId?: string;
|
|
42
|
+
chainId?: string;
|
|
43
|
+
isOWSWallet: boolean;
|
|
44
|
+
worldIdVerified: boolean;
|
|
45
|
+
webBotAuthVerified: boolean;
|
|
46
|
+
nullifierHash?: string;
|
|
47
|
+
trustScore: number;
|
|
48
|
+
breakdown: TrustBreakdown;
|
|
49
|
+
tier: string;
|
|
50
|
+
totalRequests: number;
|
|
51
|
+
successfulRequests: number;
|
|
52
|
+
failedRequests: number;
|
|
53
|
+
dailySpent: number;
|
|
54
|
+
dailyDate: string;
|
|
55
|
+
consecutiveApprovals: number;
|
|
56
|
+
consecutiveDenials: number;
|
|
57
|
+
totalApproved: number;
|
|
58
|
+
totalDenied: number;
|
|
59
|
+
humanOverrides: number;
|
|
60
|
+
counterparties: string[];
|
|
61
|
+
pendingOverride?: {
|
|
62
|
+
tx: PolicyContext['transaction'];
|
|
63
|
+
amount: number;
|
|
64
|
+
createdAt: number;
|
|
65
|
+
};
|
|
66
|
+
lastOnChainRefresh?: number;
|
|
67
|
+
requestTimestamps: number[];
|
|
68
|
+
lastActive: number;
|
|
69
|
+
consecutiveCleanDays: number;
|
|
70
|
+
cleanDayDate: string;
|
|
71
|
+
createdAt: number;
|
|
72
|
+
}
|
|
73
|
+
export interface PolicyDecisionEvent {
|
|
74
|
+
type: 'POLICY_DECISION';
|
|
75
|
+
agent: string;
|
|
76
|
+
amount: number;
|
|
77
|
+
trustScore: number;
|
|
78
|
+
tier: string;
|
|
79
|
+
decision: 'APPROVE' | 'DENY' | 'OVERRIDE';
|
|
80
|
+
reason: string;
|
|
81
|
+
dailyLimit: number;
|
|
82
|
+
dailySpent: number;
|
|
83
|
+
timestamp: number;
|
|
84
|
+
}
|
|
85
|
+
export interface TrustChangeEvent {
|
|
86
|
+
type: 'TRUST_CHANGE';
|
|
87
|
+
agent: string;
|
|
88
|
+
oldScore: number;
|
|
89
|
+
newScore: number;
|
|
90
|
+
oldTier: string;
|
|
91
|
+
newTier: string;
|
|
92
|
+
reason: string;
|
|
93
|
+
timestamp: number;
|
|
94
|
+
}
|
|
95
|
+
export interface BudgetWarningEvent {
|
|
96
|
+
type: 'BUDGET_WARNING';
|
|
97
|
+
agent: string;
|
|
98
|
+
spent: number;
|
|
99
|
+
limit: number;
|
|
100
|
+
percentage: number;
|
|
101
|
+
timestamp: number;
|
|
102
|
+
}
|
|
103
|
+
export type TriageEvent = PolicyDecisionEvent | TrustChangeEvent | BudgetWarningEvent;
|
|
104
|
+
export declare let SPENDING_TIERS: SpendingTier[];
|
|
105
|
+
export declare function updateSpendingTiers(tiers: SpendingTier[]): void;
|
|
106
|
+
export declare function getTierForScore(score: number): SpendingTier;
|
|
107
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// --- OWS Policy Types ---
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SPENDING_TIERS = void 0;
|
|
5
|
+
exports.updateSpendingTiers = updateSpendingTiers;
|
|
6
|
+
exports.getTierForScore = getTierForScore;
|
|
7
|
+
// --- Spending Tiers ---
|
|
8
|
+
exports.SPENDING_TIERS = [
|
|
9
|
+
{ name: 'Sovereign', color: '#fff8e1', dailyLimit: 1000, perTxLimit: 500, minScore: 80 },
|
|
10
|
+
{ name: 'Trusted', color: '#ffd700', dailyLimit: 200, perTxLimit: 100, minScore: 60 },
|
|
11
|
+
{ name: 'Building', color: '#4caf50', dailyLimit: 50, perTxLimit: 25, minScore: 40 },
|
|
12
|
+
{ name: 'Cautious', color: '#00bcd4', dailyLimit: 10, perTxLimit: 5, minScore: 20 },
|
|
13
|
+
{ name: 'Restricted', color: '#2196f3', dailyLimit: 2, perTxLimit: 1, minScore: 1 },
|
|
14
|
+
{ name: 'Frozen', color: '#1a1a4e', dailyLimit: 0, perTxLimit: 0, minScore: 0 },
|
|
15
|
+
];
|
|
16
|
+
function updateSpendingTiers(tiers) {
|
|
17
|
+
exports.SPENDING_TIERS = tiers;
|
|
18
|
+
}
|
|
19
|
+
function getTierForScore(score) {
|
|
20
|
+
for (const tier of exports.SPENDING_TIERS) {
|
|
21
|
+
if (score >= tier.minScore)
|
|
22
|
+
return tier;
|
|
23
|
+
}
|
|
24
|
+
return exports.SPENDING_TIERS[exports.SPENDING_TIERS.length - 1];
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA,2BAA2B;;;AA+H3B,kDAEC;AAED,0CAKC;AApBD,yBAAyB;AAEd,QAAA,cAAc,GAAmB;IAC1C,EAAE,IAAI,EAAE,WAAW,EAAI,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC1F,EAAE,IAAI,EAAE,SAAS,EAAM,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAG,UAAU,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE;IAC1F,EAAE,IAAI,EAAE,UAAU,EAAK,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAI,UAAU,EAAE,EAAE,EAAG,QAAQ,EAAE,EAAE,EAAE;IAC1F,EAAE,IAAI,EAAE,UAAU,EAAK,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAI,UAAU,EAAE,CAAC,EAAI,QAAQ,EAAE,EAAE,EAAE;IAC1F,EAAE,IAAI,EAAE,YAAY,EAAG,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,EAAK,UAAU,EAAE,CAAC,EAAI,QAAQ,EAAE,CAAC,EAAG;IAC1F,EAAE,IAAI,EAAE,QAAQ,EAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC,EAAK,UAAU,EAAE,CAAC,EAAI,QAAQ,EAAE,CAAC,EAAG;CAC3F,CAAA;AAED,SAAgB,mBAAmB,CAAC,KAAqB;IACvD,sBAAc,GAAG,KAAK,CAAA;AACxB,CAAC;AAED,SAAgB,eAAe,CAAC,KAAa;IAC3C,KAAK,MAAM,IAAI,IAAI,sBAAc,EAAE,CAAC;QAClC,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAA;IACzC,CAAC;IACD,OAAO,sBAAc,CAAC,sBAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface SignatureComponents {
|
|
2
|
+
signatureInput: string;
|
|
3
|
+
signature: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Parse RFC 9421 Signature-Input header.
|
|
7
|
+
* Format: sig1=("@method" "@authority" "@path"); keyid="agent-key"; alg="ed25519"; created=1234567890
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseSignatureInput(input: string): {
|
|
10
|
+
label: string;
|
|
11
|
+
components: string[];
|
|
12
|
+
params: Record<string, string>;
|
|
13
|
+
} | null;
|
|
14
|
+
/**
|
|
15
|
+
* Build the RFC 9421 signature base string from request components.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildSignatureBase(components: string[], request: {
|
|
18
|
+
method: string;
|
|
19
|
+
url: string;
|
|
20
|
+
headers: Record<string, string>;
|
|
21
|
+
}, signatureInputValue: string, label: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Verify an Ed25519 signature.
|
|
24
|
+
* Returns true if the signature is valid for the given public key and data.
|
|
25
|
+
*/
|
|
26
|
+
export declare function verifyEd25519Signature(publicKeyBase64: string, signatureBase64: string, data: string): boolean;
|
|
27
|
+
export declare function registerAgentPublicKey(agentKey: string, publicKeyBase64: string): void;
|
|
28
|
+
export declare function getAgentPublicKey(agentKey: string): string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* Full Web Bot Auth verification.
|
|
31
|
+
* Checks Signature-Input + Signature headers against agent's registered public key.
|
|
32
|
+
*/
|
|
33
|
+
export declare function verifyWebBotAuth(headers: Record<string, string>, request: {
|
|
34
|
+
method: string;
|
|
35
|
+
url: string;
|
|
36
|
+
headers: Record<string, string>;
|
|
37
|
+
}, agentKey: string): boolean;
|
|
38
|
+
//# sourceMappingURL=webbotauth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webbotauth.d.ts","sourceRoot":"","sources":["../src/webbotauth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B,GAAG,IAAI,CAeP;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAAE,EACpB,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACzE,mBAAmB,EAAE,MAAM,EAC3B,KAAK,EAAE,MAAM,GACZ,MAAM,CAaR;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,GACX,OAAO,CAiBT;AAKD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAEtF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEtE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EACzE,QAAQ,EAAE,MAAM,GACf,OAAO,CA0BT"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseSignatureInput = parseSignatureInput;
|
|
7
|
+
exports.buildSignatureBase = buildSignatureBase;
|
|
8
|
+
exports.verifyEd25519Signature = verifyEd25519Signature;
|
|
9
|
+
exports.registerAgentPublicKey = registerAgentPublicKey;
|
|
10
|
+
exports.getAgentPublicKey = getAgentPublicKey;
|
|
11
|
+
exports.verifyWebBotAuth = verifyWebBotAuth;
|
|
12
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
13
|
+
/**
|
|
14
|
+
* Parse RFC 9421 Signature-Input header.
|
|
15
|
+
* Format: sig1=("@method" "@authority" "@path"); keyid="agent-key"; alg="ed25519"; created=1234567890
|
|
16
|
+
*/
|
|
17
|
+
function parseSignatureInput(input) {
|
|
18
|
+
try {
|
|
19
|
+
const match = input.match(/^(\w+)=\(([^)]*)\);\s*(.+)$/);
|
|
20
|
+
if (!match)
|
|
21
|
+
return null;
|
|
22
|
+
const [, label, componentStr, paramStr] = match;
|
|
23
|
+
const components = componentStr.match(/"([^"]+)"/g)?.map(s => s.replace(/"/g, '')) ?? [];
|
|
24
|
+
const params = {};
|
|
25
|
+
for (const part of paramStr.split(';')) {
|
|
26
|
+
const [k, v] = part.trim().split('=');
|
|
27
|
+
if (k && v)
|
|
28
|
+
params[k.trim()] = v.replace(/"/g, '').trim();
|
|
29
|
+
}
|
|
30
|
+
return { label, components, params };
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the RFC 9421 signature base string from request components.
|
|
38
|
+
*/
|
|
39
|
+
function buildSignatureBase(components, request, signatureInputValue, label) {
|
|
40
|
+
const lines = [];
|
|
41
|
+
const url = new URL(request.url, 'http://localhost');
|
|
42
|
+
for (const comp of components) {
|
|
43
|
+
if (comp === '@method')
|
|
44
|
+
lines.push(`"@method": ${request.method.toUpperCase()}`);
|
|
45
|
+
else if (comp === '@authority')
|
|
46
|
+
lines.push(`"@authority": ${url.host}`);
|
|
47
|
+
else if (comp === '@path')
|
|
48
|
+
lines.push(`"@path": ${url.pathname}`);
|
|
49
|
+
else if (comp === '@query')
|
|
50
|
+
lines.push(`"@query": ?${url.search.slice(1)}`);
|
|
51
|
+
else if (comp.startsWith('@'))
|
|
52
|
+
lines.push(`"${comp}": `);
|
|
53
|
+
else
|
|
54
|
+
lines.push(`"${comp}": ${request.headers[comp.toLowerCase()] ?? ''}`);
|
|
55
|
+
}
|
|
56
|
+
lines.push(`"@signature-params": ${signatureInputValue.split('=').slice(1).join('=')}`);
|
|
57
|
+
return lines.join('\n');
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Verify an Ed25519 signature.
|
|
61
|
+
* Returns true if the signature is valid for the given public key and data.
|
|
62
|
+
*/
|
|
63
|
+
function verifyEd25519Signature(publicKeyBase64, signatureBase64, data) {
|
|
64
|
+
try {
|
|
65
|
+
const pubKeyBuffer = Buffer.from(publicKeyBase64, 'base64');
|
|
66
|
+
const sigBuffer = Buffer.from(signatureBase64, 'base64');
|
|
67
|
+
const key = crypto_1.default.createPublicKey({
|
|
68
|
+
key: Buffer.concat([
|
|
69
|
+
// Ed25519 DER prefix
|
|
70
|
+
Buffer.from('302a300506032b6570032100', 'hex'),
|
|
71
|
+
pubKeyBuffer
|
|
72
|
+
]),
|
|
73
|
+
format: 'der',
|
|
74
|
+
type: 'spki'
|
|
75
|
+
});
|
|
76
|
+
return crypto_1.default.verify(null, Buffer.from(data), key, sigBuffer);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Public key cache: agentKey -> base64 public key
|
|
83
|
+
const keyCache = new Map();
|
|
84
|
+
function registerAgentPublicKey(agentKey, publicKeyBase64) {
|
|
85
|
+
keyCache.set(agentKey, publicKeyBase64);
|
|
86
|
+
}
|
|
87
|
+
function getAgentPublicKey(agentKey) {
|
|
88
|
+
return keyCache.get(agentKey);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Full Web Bot Auth verification.
|
|
92
|
+
* Checks Signature-Input + Signature headers against agent's registered public key.
|
|
93
|
+
*/
|
|
94
|
+
function verifyWebBotAuth(headers, request, agentKey) {
|
|
95
|
+
const signatureInput = headers['signature-input'];
|
|
96
|
+
const signature = headers['signature'];
|
|
97
|
+
if (!signatureInput || !signature)
|
|
98
|
+
return false;
|
|
99
|
+
const parsed = parseSignatureInput(signatureInput);
|
|
100
|
+
if (!parsed)
|
|
101
|
+
return false;
|
|
102
|
+
// Check algorithm is ed25519
|
|
103
|
+
if (parsed.params.alg && parsed.params.alg !== 'ed25519')
|
|
104
|
+
return false;
|
|
105
|
+
// Check expiry
|
|
106
|
+
if (parsed.params.expires && Number(parsed.params.expires) < Date.now() / 1000)
|
|
107
|
+
return false;
|
|
108
|
+
// Get public key
|
|
109
|
+
const pubKey = getAgentPublicKey(agentKey) || getAgentPublicKey(parsed.params.keyid);
|
|
110
|
+
if (!pubKey)
|
|
111
|
+
return false;
|
|
112
|
+
// Build signature base and verify
|
|
113
|
+
const base = buildSignatureBase(parsed.components, request, signatureInput, parsed.label);
|
|
114
|
+
// Extract signature value (format: sig1=:base64:)
|
|
115
|
+
const sigMatch = signature.match(new RegExp(`${parsed.label}=:([^:]+):`));
|
|
116
|
+
if (!sigMatch)
|
|
117
|
+
return false;
|
|
118
|
+
return verifyEd25519Signature(pubKey, sigMatch[1], base);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=webbotauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webbotauth.js","sourceRoot":"","sources":["../src/webbotauth.ts"],"names":[],"mappings":"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"}
|
package/dist/xmtp.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function initXMTP(): Promise<void>;
|
|
2
|
+
export declare function notifyDenial(agent: string, amount: number, trustScore: number, tier: string, dailyLimit: number, dailySpent: number, reason: string, txTo: string): Promise<void>;
|
|
3
|
+
export declare function notifyTrustChange(agent: string, oldScore: number, newScore: number, oldTier: string, newTier: string, reason: string): Promise<void>;
|
|
4
|
+
export declare function notifyBudgetWarning(agent: string, spent: number, limit: number): Promise<void>;
|
|
5
|
+
export declare function notifyAnomaly(agent: string, pattern: string, action: string, riskPenalty: number, oldScore: number, newScore: number): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=xmtp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xmtp.d.ts","sourceRoot":"","sources":["../src/xmtp.ts"],"names":[],"mappings":"AAoEA,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CA0C9C;AAED,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
package/dist/xmtp.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.initXMTP = initXMTP;
|
|
4
|
+
exports.notifyDenial = notifyDenial;
|
|
5
|
+
exports.notifyTrustChange = notifyTrustChange;
|
|
6
|
+
exports.notifyBudgetWarning = notifyBudgetWarning;
|
|
7
|
+
exports.notifyAnomaly = notifyAnomaly;
|
|
8
|
+
const node_sdk_1 = require("@xmtp/node-sdk");
|
|
9
|
+
const accounts_1 = require("viem/accounts");
|
|
10
|
+
const viem_1 = require("viem");
|
|
11
|
+
const config_1 = require("./config");
|
|
12
|
+
const HUMAN_ADDRESS = process.env.HUMAN_XMTP_ADDRESS || '';
|
|
13
|
+
// Module-level state
|
|
14
|
+
let xmtpClient = null;
|
|
15
|
+
let humanDm = null;
|
|
16
|
+
let lastDeniedAgent = '';
|
|
17
|
+
function humanIdentifier() {
|
|
18
|
+
return {
|
|
19
|
+
identifier: HUMAN_ADDRESS.toLowerCase(),
|
|
20
|
+
identifierKind: 0 /* IdentifierKind.Ethereum */,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
// ----------------------------------------------------------------
|
|
24
|
+
// Incoming message listener — detects "override" replies
|
|
25
|
+
// ----------------------------------------------------------------
|
|
26
|
+
async function startMessageListener() {
|
|
27
|
+
if (!xmtpClient)
|
|
28
|
+
return;
|
|
29
|
+
try {
|
|
30
|
+
const stream = await xmtpClient.conversations.streamAllMessages();
|
|
31
|
+
for await (const message of stream) {
|
|
32
|
+
try {
|
|
33
|
+
const text = typeof message.content === 'string' ? message.content.trim().toLowerCase() : '';
|
|
34
|
+
if (text.startsWith('override') && lastDeniedAgent) {
|
|
35
|
+
const port = process.env.PORT || '4021';
|
|
36
|
+
const agentToOverride = lastDeniedAgent;
|
|
37
|
+
lastDeniedAgent = '';
|
|
38
|
+
const res = await fetch(`http://localhost:${port}/api/override/${agentToOverride}`, { method: 'POST' });
|
|
39
|
+
if (res.ok) {
|
|
40
|
+
console.log(`[XMTP] Override accepted for agent: ${agentToOverride}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
console.error('[XMTP] Error processing message:', err);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error('[XMTP] Message stream error:', err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ----------------------------------------------------------------
|
|
54
|
+
// Send helper — finds or creates DM with human, caches it
|
|
55
|
+
// ----------------------------------------------------------------
|
|
56
|
+
async function sendToHuman(text) {
|
|
57
|
+
if (!xmtpClient)
|
|
58
|
+
return;
|
|
59
|
+
try {
|
|
60
|
+
if (!humanDm) {
|
|
61
|
+
humanDm = await xmtpClient.conversations.createDmWithIdentifier(humanIdentifier());
|
|
62
|
+
}
|
|
63
|
+
await humanDm.sendText(text);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
console.error('[XMTP] Send error:', err);
|
|
67
|
+
humanDm = null; // reset so next call retries
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// ----------------------------------------------------------------
|
|
71
|
+
// Exported functions
|
|
72
|
+
// ----------------------------------------------------------------
|
|
73
|
+
async function initXMTP() {
|
|
74
|
+
if (!(0, config_1.getConfig)().xmtp?.enabled) {
|
|
75
|
+
console.log('[XMTP] Disabled by config');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const privateKey = process.env.XMTP_PRIVATE_KEY;
|
|
79
|
+
if (!privateKey || !HUMAN_ADDRESS) {
|
|
80
|
+
console.log('[XMTP] Not configured — notifications disabled');
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const account = (0, accounts_1.privateKeyToAccount)(privateKey);
|
|
85
|
+
const signer = {
|
|
86
|
+
type: 'EOA',
|
|
87
|
+
getIdentifier: () => ({
|
|
88
|
+
identifier: account.address.toLowerCase(),
|
|
89
|
+
identifierKind: 0 /* IdentifierKind.Ethereum */,
|
|
90
|
+
}),
|
|
91
|
+
signMessage: async (message) => {
|
|
92
|
+
const sig = await account.signMessage({ message });
|
|
93
|
+
return (0, viem_1.hexToBytes)(sig);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
// `as any` required: ClientOptions is a union type that doesn't survive
|
|
97
|
+
// Omit<> in Client.create's signature — SDK types collapse the union.
|
|
98
|
+
xmtpClient = await node_sdk_1.Client.create(signer, { env: 'production' });
|
|
99
|
+
// Warm up the DM channel
|
|
100
|
+
humanDm = await xmtpClient.conversations.createDmWithIdentifier(humanIdentifier());
|
|
101
|
+
// Start listening for override replies (fire and forget)
|
|
102
|
+
startMessageListener().catch((err) => {
|
|
103
|
+
console.error('[XMTP] Listener startup error:', err);
|
|
104
|
+
});
|
|
105
|
+
console.log('[XMTP] Initialized — notifications enabled');
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
console.error('[XMTP] Init failed:', err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async function notifyDenial(agent, amount, trustScore, tier, dailyLimit, dailySpent, reason, txTo) {
|
|
112
|
+
if (!xmtpClient)
|
|
113
|
+
return;
|
|
114
|
+
lastDeniedAgent = agent;
|
|
115
|
+
const msg = [
|
|
116
|
+
`⚠️ Transaction denied`,
|
|
117
|
+
`Agent: ${agent}`,
|
|
118
|
+
`Amount: $${amount.toFixed(2)} USDC → ${txTo}`,
|
|
119
|
+
`Trust score: ${trustScore} (${tier} tier)`,
|
|
120
|
+
`Daily limit: $${dailyLimit} | Spent today: $${dailySpent.toFixed(2)}`,
|
|
121
|
+
`Reason: ${reason}`,
|
|
122
|
+
``,
|
|
123
|
+
`Reply "override" to approve this transaction.`,
|
|
124
|
+
].join('\n');
|
|
125
|
+
await sendToHuman(msg);
|
|
126
|
+
}
|
|
127
|
+
async function notifyTrustChange(agent, oldScore, newScore, oldTier, newTier, reason) {
|
|
128
|
+
if (!xmtpClient)
|
|
129
|
+
return;
|
|
130
|
+
const msg = [
|
|
131
|
+
`📊 Trust update: ${agent}`,
|
|
132
|
+
`Score: ${oldScore} → ${newScore} | Tier: ${oldTier} → ${newTier}`,
|
|
133
|
+
`Reason: ${reason}`,
|
|
134
|
+
].join('\n');
|
|
135
|
+
await sendToHuman(msg);
|
|
136
|
+
}
|
|
137
|
+
async function notifyBudgetWarning(agent, spent, limit) {
|
|
138
|
+
if (!xmtpClient)
|
|
139
|
+
return;
|
|
140
|
+
const percentage = Math.round((spent / limit) * 100);
|
|
141
|
+
const remaining = limit - spent;
|
|
142
|
+
const msg = [
|
|
143
|
+
`💰 Budget alert: ${agent}`,
|
|
144
|
+
`Daily spend: $${spent.toFixed(2)} / $${limit} (${percentage}%)`,
|
|
145
|
+
`Remaining: $${remaining.toFixed(2)}`,
|
|
146
|
+
].join('\n');
|
|
147
|
+
await sendToHuman(msg);
|
|
148
|
+
}
|
|
149
|
+
async function notifyAnomaly(agent, pattern, action, riskPenalty, oldScore, newScore) {
|
|
150
|
+
if (!xmtpClient)
|
|
151
|
+
return;
|
|
152
|
+
const msg = [
|
|
153
|
+
`🚨 Anomaly: ${agent}`,
|
|
154
|
+
`Pattern: ${pattern}`,
|
|
155
|
+
`Action: ${action}`,
|
|
156
|
+
`Risk penalty: -${riskPenalty}`,
|
|
157
|
+
`Trust score: ${oldScore} → ${newScore}`,
|
|
158
|
+
].join('\n');
|
|
159
|
+
await sendToHuman(msg);
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=xmtp.js.map
|
package/dist/xmtp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xmtp.js","sourceRoot":"","sources":["../src/xmtp.ts"],"names":[],"mappings":";;AAoEA,4BA0CC;AAED,oCAuBC;AAED,8CAeC;AAED,kDAcC;AAED,sCAiBC;AA3LD,6CAAuD;AAEvD,4CAAmD;AACnD,+BAAiC;AACjC,qCAAoC;AAEpC,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,EAAE,CAAA;AAE1D,qBAAqB;AACrB,IAAI,UAAU,GAAkB,IAAI,CAAA;AACpC,IAAI,OAAO,GAAc,IAAI,CAAA;AAC7B,IAAI,eAAe,GAAG,EAAE,CAAA;AAExB,SAAS,eAAe;IACtB,OAAO;QACL,UAAU,EAAE,aAAa,CAAC,WAAW,EAAE;QACvC,cAAc,iCAAyB;KACxC,CAAA;AACH,CAAC;AAED,mEAAmE;AACnE,yDAAyD;AACzD,mEAAmE;AACnE,KAAK,UAAU,oBAAoB;IACjC,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,iBAAiB,EAAE,CAAA;QACjE,IAAI,KAAK,EAAE,MAAM,OAAO,IAAI,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;gBAC5F,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,eAAe,EAAE,CAAC;oBACnD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAA;oBACvC,MAAM,eAAe,GAAG,eAAe,CAAA;oBACvC,eAAe,GAAG,EAAE,CAAA;oBACpB,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,iBAAiB,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;oBACvG,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;wBACX,OAAO,CAAC,GAAG,CAAC,uCAAuC,eAAe,EAAE,CAAC,CAAA;oBACvE,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,GAAG,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAA;IACpD,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,0DAA0D;AAC1D,mEAAmE;AACnE,KAAK,UAAU,WAAW,CAAC,IAAY;IACrC,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,sBAAsB,CAAC,eAAe,EAAE,CAAC,CAAA;QACpF,CAAC;QACD,MAAM,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAA;QACxC,OAAO,GAAG,IAAI,CAAA,CAAC,6BAA6B;IAC9C,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,qBAAqB;AACrB,mEAAmE;AAE5D,KAAK,UAAU,QAAQ;IAC5B,IAAI,CAAC,IAAA,kBAAS,GAAE,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;QACxC,OAAM;IACR,CAAC;IACD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAC/C,IAAI,CAAC,UAAU,IAAI,CAAC,aAAa,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAA;QAC7D,OAAM;IACR,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,8BAAmB,EAAC,UAA2B,CAAC,CAAA;QAEhE,MAAM,MAAM,GAAW;YACrB,IAAI,EAAE,KAAK;YACX,aAAa,EAAE,GAAe,EAAE,CAAC,CAAC;gBAChC,UAAU,EAAE,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE;gBACzC,cAAc,iCAAyB;aACxC,CAAC;YACF,WAAW,EAAE,KAAK,EAAE,OAAe,EAAuB,EAAE;gBAC1D,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;gBAClD,OAAO,IAAA,iBAAU,EAAC,GAAG,CAAC,CAAA;YACxB,CAAC;SACF,CAAA;QAED,wEAAwE;QACxE,sEAAsE;QACtE,UAAU,GAAG,MAAM,iBAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,EAAS,CAAC,CAAA;QAEtE,yBAAyB;QACzB,OAAO,GAAG,MAAM,UAAU,CAAC,aAAa,CAAC,sBAAsB,CAAC,eAAe,EAAE,CAAC,CAAA;QAElF,yDAAyD;QACzD,oBAAoB,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACnC,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAA;QACtD,CAAC,CAAC,CAAA;QAEF,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;IAC3D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAA;IAC3C,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,KAAa,EACb,MAAc,EACd,UAAkB,EAClB,IAAY,EACZ,UAAkB,EAClB,UAAkB,EAClB,MAAc,EACd,IAAY;IAEZ,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,eAAe,GAAG,KAAK,CAAA;IACvB,MAAM,GAAG,GAAG;QACV,uBAAuB;QACvB,UAAU,KAAK,EAAE;QACjB,YAAY,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE;QAC9C,gBAAgB,UAAU,KAAK,IAAI,QAAQ;QAC3C,iBAAiB,UAAU,oBAAoB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;QACtE,WAAW,MAAM,EAAE;QACnB,EAAE;QACF,+CAA+C;KAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAEM,KAAK,UAAU,iBAAiB,CACrC,KAAa,EACb,QAAgB,EAChB,QAAgB,EAChB,OAAe,EACf,OAAe,EACf,MAAc;IAEd,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,MAAM,GAAG,GAAG;QACV,oBAAoB,KAAK,EAAE;QAC3B,UAAU,QAAQ,MAAM,QAAQ,YAAY,OAAO,MAAM,OAAO,EAAE;QAClE,WAAW,MAAM,EAAE;KACpB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAEM,KAAK,UAAU,mBAAmB,CACvC,KAAa,EACb,KAAa,EACb,KAAa;IAEb,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAA;IACpD,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAA;IAC/B,MAAM,GAAG,GAAG;QACV,oBAAoB,KAAK,EAAE;QAC3B,iBAAiB,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,UAAU,IAAI;QAChE,eAAe,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;KACtC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAEM,KAAK,UAAU,aAAa,CACjC,KAAa,EACb,OAAe,EACf,MAAc,EACd,WAAmB,EACnB,QAAgB,EAChB,QAAgB;IAEhB,IAAI,CAAC,UAAU;QAAE,OAAM;IACvB,MAAM,GAAG,GAAG;QACV,eAAe,KAAK,EAAE;QACtB,YAAY,OAAO,EAAE;QACrB,WAAW,MAAM,EAAE;QACnB,kBAAkB,WAAW,EAAE;QAC/B,gBAAgB,QAAQ,MAAM,QAAQ,EAAE;KACzC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACZ,MAAM,WAAW,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "triage-ows",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reputation-gated spending policies for OWS agent wallets",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"triage-ows": "./bin/triage-ows.js",
|
|
9
|
+
"triage-policy": "./bin/triage-policy.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"src/",
|
|
14
|
+
"dist/",
|
|
15
|
+
"dashboard-dist/",
|
|
16
|
+
"triage.config.example.json",
|
|
17
|
+
"policy-template.json",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"seed": "npx tsx scripts/seed-demo.ts",
|
|
24
|
+
"simulate": "npx tsx scripts/simulate.ts"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"hono": ">=4.0.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@hono/node-server": "^1.19.12",
|
|
31
|
+
"@worldcoin/idkit-server": "^1.0.0",
|
|
32
|
+
"@xmtp/node-sdk": "^6.0.0",
|
|
33
|
+
"viem": "^2.47.6",
|
|
34
|
+
"ws": "^8.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"@types/ws": "^8.0.0",
|
|
39
|
+
"hono": "^4.12.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"ows",
|
|
44
|
+
"open-wallet-standard",
|
|
45
|
+
"policy",
|
|
46
|
+
"trust",
|
|
47
|
+
"reputation",
|
|
48
|
+
"agent",
|
|
49
|
+
"spending-governance",
|
|
50
|
+
"hono"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "https://github.com/ioannisCC/triage-hackathon"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/ioannisCC/triage-hackathon#readme"
|
|
58
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "triage-trust",
|
|
3
|
+
"name": "Triage Reputation-Gated Policy",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"created_at": "2026-04-04T00:00:00Z",
|
|
6
|
+
"rules": [
|
|
7
|
+
{ "type": "allowed_chains", "chain_ids": ["eip155:84532"] }
|
|
8
|
+
],
|
|
9
|
+
"executable": "./node_modules/.bin/triage-policy",
|
|
10
|
+
"config": {
|
|
11
|
+
"scoring_server": "http://localhost:4021"
|
|
12
|
+
},
|
|
13
|
+
"action": "deny"
|
|
14
|
+
}
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { readFileSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import type { SpendingTier } from './types'
|
|
4
|
+
import { updateSpendingTiers } from './types'
|
|
5
|
+
|
|
6
|
+
export interface TriageConfig {
|
|
7
|
+
scoreBands?: Array<{
|
|
8
|
+
name: string
|
|
9
|
+
min: number
|
|
10
|
+
dailyLimit: number
|
|
11
|
+
perTxLimit: number
|
|
12
|
+
color?: string
|
|
13
|
+
}>
|
|
14
|
+
|
|
15
|
+
warningThreshold?: number
|
|
16
|
+
|
|
17
|
+
xmtp?: { enabled: boolean }
|
|
18
|
+
worldId?: { enabled: boolean }
|
|
19
|
+
webBotAuth?: { enabled: boolean }
|
|
20
|
+
networkScore?: { enabled: boolean }
|
|
21
|
+
|
|
22
|
+
scoring?: {
|
|
23
|
+
overrideBoost?: number
|
|
24
|
+
maxFrequencyPenalty?: number
|
|
25
|
+
inactivityDecayRate?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
port?: number
|
|
29
|
+
dashboardEnabled?: boolean
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const DEFAULT_CONFIG: Required<TriageConfig> = {
|
|
33
|
+
scoreBands: [
|
|
34
|
+
{ name: 'Sovereign', min: 80, dailyLimit: 1000, perTxLimit: 500, color: '#fff8e1' },
|
|
35
|
+
{ name: 'Trusted', min: 60, dailyLimit: 200, perTxLimit: 100, color: '#ffd700' },
|
|
36
|
+
{ name: 'Building', min: 40, dailyLimit: 50, perTxLimit: 25, color: '#4caf50' },
|
|
37
|
+
{ name: 'Cautious', min: 20, dailyLimit: 10, perTxLimit: 5, color: '#00bcd4' },
|
|
38
|
+
{ name: 'Restricted', min: 1, dailyLimit: 2, perTxLimit: 1, color: '#2196f3' },
|
|
39
|
+
{ name: 'Frozen', min: 0, dailyLimit: 0, perTxLimit: 0, color: '#1a1a4e' },
|
|
40
|
+
],
|
|
41
|
+
warningThreshold: 0.8,
|
|
42
|
+
xmtp: { enabled: true },
|
|
43
|
+
worldId: { enabled: true },
|
|
44
|
+
webBotAuth: { enabled: true },
|
|
45
|
+
networkScore: { enabled: true },
|
|
46
|
+
scoring: { overrideBoost: 3, maxFrequencyPenalty: 10, inactivityDecayRate: 0.5 },
|
|
47
|
+
port: 4021,
|
|
48
|
+
dashboardEnabled: true,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let _config: Required<TriageConfig> = { ...DEFAULT_CONFIG }
|
|
52
|
+
|
|
53
|
+
export function loadConfig(configPath?: string): Required<TriageConfig> {
|
|
54
|
+
const path = configPath || process.env.TRIAGE_CONFIG_PATH || join(process.cwd(), 'triage.config.json')
|
|
55
|
+
try {
|
|
56
|
+
const raw = readFileSync(path, 'utf-8')
|
|
57
|
+
const parsed = JSON.parse(raw) as TriageConfig
|
|
58
|
+
_config = {
|
|
59
|
+
...DEFAULT_CONFIG,
|
|
60
|
+
...parsed,
|
|
61
|
+
xmtp: { ...DEFAULT_CONFIG.xmtp, ...parsed.xmtp },
|
|
62
|
+
worldId: { ...DEFAULT_CONFIG.worldId, ...parsed.worldId },
|
|
63
|
+
webBotAuth: { ...DEFAULT_CONFIG.webBotAuth, ...parsed.webBotAuth },
|
|
64
|
+
networkScore: { ...DEFAULT_CONFIG.networkScore, ...parsed.networkScore },
|
|
65
|
+
scoring: { ...DEFAULT_CONFIG.scoring, ...parsed.scoring },
|
|
66
|
+
}
|
|
67
|
+
if (parsed.scoreBands) {
|
|
68
|
+
_config.scoreBands = parsed.scoreBands
|
|
69
|
+
updateSpendingTiers(parsed.scoreBands.map(b => ({
|
|
70
|
+
name: b.name as SpendingTier['name'],
|
|
71
|
+
color: b.color || '#666',
|
|
72
|
+
dailyLimit: b.dailyLimit,
|
|
73
|
+
perTxLimit: b.perTxLimit,
|
|
74
|
+
minScore: b.min,
|
|
75
|
+
})))
|
|
76
|
+
}
|
|
77
|
+
console.log(`[Config] Loaded from ${path}`)
|
|
78
|
+
} catch {
|
|
79
|
+
console.log('[Config] No triage.config.json found — using defaults')
|
|
80
|
+
}
|
|
81
|
+
return _config
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function getConfig(): Required<TriageConfig> {
|
|
85
|
+
return _config
|
|
86
|
+
}
|