meshsig 0.5.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.
@@ -0,0 +1,190 @@
1
+ #!/bin/bash
2
+ # ==============================================================================
3
+ # MeshSig Invoke — Secure agent-to-agent delegation
4
+ # Replaces invoke.sh with cryptographically signed communication.
5
+ # Every delegation is signed with Ed25519 and logged to MeshSig.
6
+ # ==============================================================================
7
+
8
+ TARGET="$1"
9
+ MESSAGE="$2"
10
+ CONTEXT="${3:-}"
11
+
12
+ if [ -z "$TARGET" ] || [ -z "$MESSAGE" ]; then
13
+ echo '{"error":"Usage: invoke-mesh.sh <client_name> <message> [context]"}'
14
+ exit 1
15
+ fi
16
+
17
+ MESH_URL="${MESHSIG_URL:-http://127.0.0.1:4888}"
18
+ GATEWAY_URL="${GATEWAY_URL:-http://127.0.0.1:3001}"
19
+ GATEWAY_SECRET="${GATEWAY_SECRET:-xrunly-secret-change-me}"
20
+
21
+ # Agent identity file (created on first run by setup)
22
+ IDENTITY_DIR="/opt/meshsig/identities"
23
+ CALLER_NAME="${OPENCLAW_AGENT_NAME:-$(readlink -f "$SCRIPT_DIR" | sed "s|.*clients/||" | cut -d/ -f1)}"
24
+ CALLER_IDENTITY="$IDENTITY_DIR/$CALLER_NAME.json"
25
+ TARGET_IDENTITY="$IDENTITY_DIR/$TARGET.json"
26
+
27
+ # ---- Ensure identities exist ------------------------------------------------
28
+
29
+ ensure_identity() {
30
+ local agent_name="$1"
31
+ local identity_file="$IDENTITY_DIR/$agent_name.json"
32
+
33
+ if [ ! -f "$identity_file" ]; then
34
+ # Register with MeshSig and save identity
35
+ local result=$(curl -s -X POST "$MESH_URL/agents/register" \
36
+ -H "Content-Type: application/json" \
37
+ -d "{\"name\":\"$agent_name\",\"capabilities\":[]}")
38
+
39
+ echo "$result" | python3 -c "
40
+ import sys, json, os
41
+ data = json.load(sys.stdin)
42
+ identity = data.get('identity', {})
43
+ record = data.get('record', {})
44
+ out = {
45
+ 'did': identity.get('did',''),
46
+ 'privateKey': identity.get('privateKey',''),
47
+ 'publicKey': identity.get('publicKey',''),
48
+ 'displayName': record.get('displayName', '$agent_name')
49
+ }
50
+ os.makedirs('$IDENTITY_DIR', exist_ok=True)
51
+ with open('$identity_file', 'w') as f:
52
+ json.dump(out, f, indent=2)
53
+ print(json.dumps({'registered': out['did']}))" 2>/dev/null
54
+ fi
55
+ }
56
+
57
+ mkdir -p "$IDENTITY_DIR"
58
+ ensure_identity "$CALLER_NAME"
59
+ ensure_identity "$TARGET"
60
+
61
+ # ---- Read identities --------------------------------------------------------
62
+
63
+ CALLER_DID=$(python3 -c "import json; print(json.load(open('$CALLER_IDENTITY'))['did'])" 2>/dev/null)
64
+ CALLER_KEY=$(python3 -c "import json; print(json.load(open('$CALLER_IDENTITY'))['privateKey'])" 2>/dev/null)
65
+ TARGET_DID=$(python3 -c "import json; print(json.load(open('$TARGET_IDENTITY'))['did'])" 2>/dev/null)
66
+
67
+ if [ -z "$CALLER_DID" ] || [ -z "$TARGET_DID" ]; then
68
+ echo '{"error":"Failed to load agent identities. Check MeshSig is running."}'
69
+ exit 1
70
+ fi
71
+
72
+ # ---- Ensure connection exists -----------------------------------------------
73
+
74
+ # Check if connection exists, create if not
75
+ CONNECTION=$(curl -s "$MESH_URL/connections?did=$CALLER_DID" | python3 -c "
76
+ import sys, json
77
+ data = json.load(sys.stdin)
78
+ conns = data.get('connections', [])
79
+ target = '$TARGET_DID'
80
+ for c in conns:
81
+ if c['agentADid'] == target or c['agentBDid'] == target:
82
+ print(c['channelId'])
83
+ break
84
+ " 2>/dev/null)
85
+
86
+ if [ -z "$CONNECTION" ]; then
87
+ # Perform handshake
88
+ CALLER_PKEY=$(python3 -c "import json; print(json.load(open('$CALLER_IDENTITY'))['privateKey'])" 2>/dev/null)
89
+ TARGET_PKEY=$(python3 -c "import json; print(json.load(open('$TARGET_IDENTITY'))['privateKey'])" 2>/dev/null)
90
+
91
+ curl -s -X POST "$MESH_URL/handshake" \
92
+ -H "Content-Type: application/json" \
93
+ -d "{\"fromDid\":\"$CALLER_DID\",\"toDid\":\"$TARGET_DID\",\"privateKeyA\":\"$CALLER_PKEY\",\"privateKeyB\":\"$TARGET_PKEY\",\"permissions\":[\"send:request\",\"execute:task\"]}" > /dev/null 2>&1
94
+ fi
95
+
96
+ # ---- Sign and send message via MeshSig ------------------------------------
97
+
98
+ # 1. Sign the message through MeshSig
99
+ FULL_MESSAGE="$MESSAGE"
100
+ if [ -n "$CONTEXT" ]; then
101
+ FULL_MESSAGE="$MESSAGE | Context: $CONTEXT"
102
+ fi
103
+
104
+ SIGN_RESULT=$(curl -s -X POST "$MESH_URL/messages/send" \
105
+ -H "Content-Type: application/json" \
106
+ -d "{
107
+ \"fromDid\": \"$CALLER_DID\",
108
+ \"toDid\": \"$TARGET_DID\",
109
+ \"message\": $(python3 -c "import json; print(json.dumps('$FULL_MESSAGE'))"),
110
+ \"privateKey\": \"$CALLER_KEY\"
111
+ }")
112
+
113
+ VERIFIED=$(echo "$SIGN_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('verified', False))" 2>/dev/null)
114
+
115
+ if [ "$VERIFIED" != "True" ]; then
116
+ echo "{\"error\":\"MeshSig signature verification failed\",\"details\":$SIGN_RESULT}"
117
+ exit 1
118
+ fi
119
+
120
+ # 2. Forward to actual agent via agent-gateway — WITH verified identity context
121
+ CALLER_DISPLAY=$(python3 -c "import json; print(json.load(open('$CALLER_IDENTITY'))['displayName'])" 2>/dev/null)
122
+ CALLER_CAPS=$(curl -s "$MESH_URL/agents/$CALLER_DID" | python3 -c "
123
+ import sys,json
124
+ try:
125
+ a = json.load(sys.stdin).get('agent',{})
126
+ caps = ', '.join([c['type'] for c in a.get('capabilities',[])])
127
+ trust = a.get('trustScore', 0)
128
+ print(f'{caps} (trust: {trust*100:.0f}%)')
129
+ except:
130
+ print('')
131
+ " 2>/dev/null)
132
+
133
+ # Build enriched message with identity
134
+ ENRICHED_MESSAGE=$(python3 -c "
135
+ import json
136
+ caller = '$CALLER_DISPLAY'
137
+ caller_did = '$CALLER_DID'
138
+ caps = '$CALLER_CAPS'
139
+ msg = '''$MESSAGE'''
140
+ ctx = '''${CONTEXT:-}'''
141
+
142
+ parts = []
143
+ parts.append(f'[MeshSig Verified Message]')
144
+ parts.append(f'From: {caller} ({caller_did[:30]}...)')
145
+ if caps:
146
+ parts.append(f'Role: {caps}')
147
+ parts.append(f'Signature: Ed25519 verified ✓')
148
+ parts.append(f'')
149
+ parts.append(f'Task: {msg}')
150
+ if ctx:
151
+ parts.append(f'Context: {ctx}')
152
+
153
+ print(json.dumps('\n'.join(parts)))
154
+ ")
155
+
156
+ RESPONSE=$(curl -s --max-time 120 -X POST "$GATEWAY_URL/invoke-agent" \
157
+ -H "Content-Type: application/json" \
158
+ -H "x-api-secret: $GATEWAY_SECRET" \
159
+ -d "{
160
+ \"target_client_name\": \"$TARGET\",
161
+ \"message\": $ENRICHED_MESSAGE,
162
+ \"context\": $(python3 -c "import json; print(json.dumps('${CONTEXT:-}'))")
163
+ }")
164
+
165
+ # 3. Log the response through MeshSig (signed by target)
166
+ TARGET_KEY=$(python3 -c "import json; print(json.load(open('$TARGET_IDENTITY'))['privateKey'])" 2>/dev/null)
167
+
168
+ RESPONSE_PREVIEW=$(echo "$RESPONSE" | python3 -c "
169
+ import sys, json
170
+ try:
171
+ data = json.load(sys.stdin)
172
+ text = data.get('response', data.get('message', str(data)))[:200]
173
+ print(text)
174
+ except:
175
+ print(sys.stdin.read()[:200])
176
+ " 2>/dev/null)
177
+
178
+ if [ -n "$RESPONSE_PREVIEW" ]; then
179
+ curl -s -X POST "$MESH_URL/messages/send" \
180
+ -H "Content-Type: application/json" \
181
+ -d "{
182
+ \"fromDid\": \"$TARGET_DID\",
183
+ \"toDid\": \"$CALLER_DID\",
184
+ \"message\": $(python3 -c "import json; print(json.dumps('$RESPONSE_PREVIEW'))"),
185
+ \"privateKey\": \"$TARGET_KEY\"
186
+ }" > /dev/null 2>&1
187
+ fi
188
+
189
+ # 4. Return the response (same format as original invoke.sh)
190
+ echo "$RESPONSE"
@@ -0,0 +1,89 @@
1
+ #!/bin/bash
2
+ # ==============================================================================
3
+ # MeshSig — Auto-register a new agent
4
+ #
5
+ # Called automatically when xrunly-api provisions a new agent.
6
+ # Usage: bash register-agent.sh <client_name>
7
+ # Example: bash register-agent.sh agent-mari---atendente-de--17734059
8
+ # ==============================================================================
9
+
10
+ CLIENT_NAME="$1"
11
+ if [ -z "$CLIENT_NAME" ]; then
12
+ echo '{"error":"client_name required"}'
13
+ exit 1
14
+ fi
15
+
16
+ MESH_URL="${MESHSIG_URL:-http://127.0.0.1:4888}"
17
+ IDENTITY_DIR="/opt/meshsig/identities"
18
+ identity_file="$IDENTITY_DIR/$CLIENT_NAME.json"
19
+
20
+ # Check MeshSig is running
21
+ if ! curl -s "$MESH_URL/health" > /dev/null 2>&1; then
22
+ echo '{"error":"meshsig not running"}'
23
+ exit 1
24
+ fi
25
+
26
+ # Already registered?
27
+ if [ -f "$identity_file" ]; then
28
+ did=$(python3 -c "import json; print(json.load(open('$identity_file'))['did'])" 2>/dev/null)
29
+ echo "{\"already_registered\":true,\"did\":\"$did\"}"
30
+ exit 0
31
+ fi
32
+
33
+ # Detect capabilities
34
+ case "$CLIENT_NAME" in
35
+ *gestor*|*gerente*|*manager*|*coo*|*ceo*)
36
+ caps='[{"type":"management","confidence":0.95},{"type":"delegation","confidence":0.9}]' ;;
37
+ *atendente*|*suporte*|*support*)
38
+ caps='[{"type":"customer-support","confidence":0.92}]' ;;
39
+ *sdr*|*vendas*|*sales*)
40
+ caps='[{"type":"sales","confidence":0.9},{"type":"sdr","confidence":0.88}]' ;;
41
+ *copy*|*redator*|*writer*|*content*)
42
+ caps='[{"type":"copywriting","confidence":0.93}]' ;;
43
+ *social*|*marketing*|*instagram*)
44
+ caps='[{"type":"social-media","confidence":0.91}]' ;;
45
+ *analista*|*analytics*|*dados*|*data*)
46
+ caps='[{"type":"analytics","confidence":0.87}]' ;;
47
+ *)
48
+ caps='[{"type":"general","confidence":0.8}]' ;;
49
+ esac
50
+
51
+ # Extract display name
52
+ display_name=$(echo "$CLIENT_NAME" | sed 's/^agent-//' | cut -d'-' -f1 | sed 's/./\U&/')
53
+
54
+ # Register
55
+ mkdir -p "$IDENTITY_DIR"
56
+ result=$(curl -s -X POST "$MESH_URL/agents/register" \
57
+ -H "Content-Type: application/json" \
58
+ -d "{\"name\":\"$display_name\",\"capabilities\":$caps}")
59
+
60
+ # Save identity file
61
+ echo "$result" | python3 -c "
62
+ import sys, json
63
+ data = json.load(sys.stdin)
64
+ identity = data.get('identity', {})
65
+ record = data.get('record', {})
66
+ out = {
67
+ 'did': identity.get('did',''),
68
+ 'privateKey': identity.get('privateKey',''),
69
+ 'publicKey': identity.get('publicKey',''),
70
+ 'displayName': record.get('displayName', ''),
71
+ 'clientName': '$CLIENT_NAME'
72
+ }
73
+ with open('$identity_file', 'w') as f:
74
+ json.dump(out, f, indent=2)
75
+ print(json.dumps({'registered':True,'did':out['did'],'name':out['displayName']}))
76
+ " 2>/dev/null
77
+
78
+ # Auto-connect to managers
79
+ for mgr_file in "$IDENTITY_DIR"/agent-*gestor*.json "$IDENTITY_DIR"/agent-*gerente*.json; do
80
+ [ -f "$mgr_file" ] || continue
81
+ mgr_did=$(python3 -c "import json; print(json.load(open('$mgr_file'))['did'])" 2>/dev/null)
82
+ mgr_key=$(python3 -c "import json; print(json.load(open('$mgr_file'))['privateKey'])" 2>/dev/null)
83
+ new_did=$(python3 -c "import json; print(json.load(open('$identity_file'))['did'])" 2>/dev/null)
84
+ new_key=$(python3 -c "import json; print(json.load(open('$identity_file'))['privateKey'])" 2>/dev/null)
85
+
86
+ curl -s -X POST "$MESH_URL/handshake" \
87
+ -H "Content-Type: application/json" \
88
+ -d "{\"fromDid\":\"$mgr_did\",\"toDid\":\"$new_did\",\"privateKeyA\":\"$mgr_key\",\"privateKeyB\":\"$new_key\"}" > /dev/null 2>&1
89
+ done
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+ # ==============================================================================
3
+ # MeshSig Uninstall — Restores original invoke.sh
4
+ # ==============================================================================
5
+
6
+ CLIENT_DIR="/root/clients"
7
+
8
+ echo ""
9
+ echo " Restoring original invoke.sh files..."
10
+ echo ""
11
+
12
+ for d in "$CLIENT_DIR"/agent-*/; do
13
+ agent_name=$(basename "$d")
14
+ skill_dir="$d/.openclaw/workspace/skills/invoke-team"
15
+ backup="$skill_dir/invoke.sh.original"
16
+
17
+ if [ -f "$backup" ]; then
18
+ cp "$backup" "$skill_dir/invoke.sh"
19
+ echo " ✓ $agent_name — restored original invoke.sh"
20
+ fi
21
+ done
22
+
23
+ echo ""
24
+ echo " Done. MeshSig signing removed. Original communication restored."
25
+ echo ""
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+ # ==============================================================================
3
+ # MeshSig — Unregister an agent
4
+ #
5
+ # Called when xrunly-api deprovisions an agent.
6
+ # Usage: bash unregister-agent.sh <client_name>
7
+ # ==============================================================================
8
+
9
+ CLIENT_NAME="$1"
10
+ if [ -z "$CLIENT_NAME" ]; then
11
+ echo '{"error":"client_name required"}'
12
+ exit 1
13
+ fi
14
+
15
+ IDENTITY_DIR="/opt/meshsig/identities"
16
+ identity_file="$IDENTITY_DIR/$CLIENT_NAME.json"
17
+
18
+ if [ -f "$identity_file" ]; then
19
+ rm -f "$identity_file"
20
+ echo "{\"unregistered\":true,\"client\":\"$CLIENT_NAME\"}"
21
+ else
22
+ echo "{\"not_found\":true,\"client\":\"$CLIENT_NAME\"}"
23
+ fi