@dp-pcs/ogp 0.2.30 → 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.
@@ -0,0 +1,448 @@
1
+ # Hermes Integration Implementation Checklist
2
+
3
+ > Developer checklist for adding Hermes support to OGP
4
+
5
+ ## Phase 1: Sidecar Integration (Week 1)
6
+
7
+ ### Prerequisites
8
+ - [ ] Hermes installed and running
9
+ - [ ] Hermes webhook adapter enabled (port 8644)
10
+ - [ ] OpenClaw+OGP setup working (baseline to not break)
11
+
12
+ ### Code Changes
13
+
14
+ #### 1. Add Hermes Notification Backend
15
+
16
+ **File:** `src/daemon/notify.ts`
17
+
18
+ - [ ] Import crypto at the top
19
+ - [ ] Add `NotificationBackend` interface
20
+ - [ ] Implement `HermesBackend` class
21
+ - [ ] Implement `OpenClawBackend` class (wrap existing code)
22
+ - [ ] Add `getNotificationBackend()` factory
23
+ - [ ] Refactor `notifyLocalAgent()` to use backend system
24
+ - [ ] Add HMAC signature generation for Hermes webhook
25
+
26
+ **Code Template:**
27
+ ```typescript
28
+ import crypto from 'crypto';
29
+
30
+ interface NotificationBackend {
31
+ name: string;
32
+ notify(context: NotificationContext): Promise<void>;
33
+ }
34
+
35
+ interface NotificationContext {
36
+ peerId: string;
37
+ peerDisplayName: string;
38
+ intent: string;
39
+ payload: any;
40
+ timestamp: string;
41
+ }
42
+
43
+ class HermesBackend implements NotificationBackend {
44
+ name = "hermes";
45
+
46
+ async notify(ctx: NotificationContext): Promise<void> {
47
+ const config = loadConfig();
48
+ const webhookUrl = config.hermesWebhookUrl || 'http://localhost:8644/webhooks/ogp_federation';
49
+ const secret = config.hermesWebhookSecret;
50
+
51
+ if (!secret) {
52
+ throw new Error('hermesWebhookSecret not configured');
53
+ }
54
+
55
+ const body = {
56
+ peer_id: ctx.peerId,
57
+ peer_display_name: ctx.peerDisplayName,
58
+ intent: ctx.intent,
59
+ topic: ctx.payload.topic || "",
60
+ message: ctx.payload.message || JSON.stringify(ctx.payload),
61
+ priority: ctx.payload.priority || "normal",
62
+ conversation_id: ctx.payload.conversationId,
63
+ timestamp: ctx.timestamp,
64
+ payload: ctx.payload
65
+ };
66
+
67
+ const bodyStr = JSON.stringify(body);
68
+ const signature = crypto
69
+ .createHmac('sha256', secret)
70
+ .update(bodyStr)
71
+ .digest('hex');
72
+
73
+ await fetch(webhookUrl, {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ 'X-Hub-Signature-256': `sha256=${signature}`
78
+ },
79
+ body: bodyStr
80
+ });
81
+ }
82
+ }
83
+
84
+ class OpenClawBackend implements NotificationBackend {
85
+ name = "openclaw";
86
+
87
+ async notify(ctx: NotificationContext): Promise<void> {
88
+ // Move existing notifyOpenClaw() code here
89
+ }
90
+ }
91
+
92
+ function getNotificationBackend(): NotificationBackend {
93
+ const config = loadConfig();
94
+ const platform = config.platform || 'openclaw';
95
+
96
+ switch (platform) {
97
+ case 'openclaw':
98
+ return new OpenClawBackend();
99
+ case 'hermes':
100
+ return new HermesBackend();
101
+ default:
102
+ throw new Error(`Unknown platform: ${platform}`);
103
+ }
104
+ }
105
+
106
+ export async function notifyLocalAgent(
107
+ peerId: string,
108
+ peerDisplayName: string,
109
+ intent: string,
110
+ payload: any
111
+ ): Promise<void> {
112
+ const backend = getNotificationBackend();
113
+
114
+ await backend.notify({
115
+ peerId,
116
+ peerDisplayName,
117
+ intent,
118
+ payload,
119
+ timestamp: new Date().toISOString()
120
+ });
121
+ }
122
+ ```
123
+
124
+ #### 2. Update Config Types
125
+
126
+ **File:** `src/shared/config.ts`
127
+
128
+ - [ ] Add `platform?: string` field to `Config` interface
129
+ - [ ] Add `hermesWebhookUrl?: string` field
130
+ - [ ] Add `hermesWebhookSecret?: string` field
131
+ - [ ] Update config validation
132
+ - [ ] Add migration for existing configs (default platform to 'openclaw')
133
+
134
+ **Code Template:**
135
+ ```typescript
136
+ export interface Config {
137
+ // Existing fields...
138
+ daemonPort: number;
139
+ gatewayUrl: string;
140
+ displayName: string;
141
+ email: string;
142
+ stateDir?: string;
143
+
144
+ // Platform selection
145
+ platform?: string; // 'openclaw' | 'hermes'
146
+
147
+ // OpenClaw-specific (existing)
148
+ openclawUrl?: string;
149
+ openclawToken?: string;
150
+ openclawHooksToken?: string;
151
+ agentId?: string;
152
+ notifyTarget?: string;
153
+ notifyTargets?: Record<string, string>;
154
+
155
+ // Hermes-specific (new)
156
+ hermesWebhookUrl?: string;
157
+ hermesWebhookSecret?: string;
158
+
159
+ // Existing fields...
160
+ rendezvous?: RendezvousConfig;
161
+ }
162
+ ```
163
+
164
+ #### 3. Update CLI Setup Wizard
165
+
166
+ **File:** `src/cli/setup.ts`
167
+
168
+ - [ ] Add platform selection prompt (OpenClaw or Hermes)
169
+ - [ ] Conditionally prompt for OpenClaw config OR Hermes config
170
+ - [ ] For Hermes: prompt for webhook URL and secret
171
+ - [ ] For OpenClaw: use existing agent discovery flow
172
+ - [ ] Save appropriate fields to config.json
173
+
174
+ **Code Template:**
175
+ ```typescript
176
+ // In setup wizard, after basic info:
177
+
178
+ const platform = await prompt({
179
+ type: 'select',
180
+ name: 'platform',
181
+ message: 'Which AI platform is this gateway for?',
182
+ choices: [
183
+ { title: 'OpenClaw', value: 'openclaw' },
184
+ { title: 'Hermes', value: 'hermes' }
185
+ ]
186
+ });
187
+
188
+ if (platform === 'hermes') {
189
+ const hermesWebhookUrl = await prompt({
190
+ type: 'text',
191
+ name: 'hermesWebhookUrl',
192
+ message: 'Hermes webhook URL',
193
+ initial: 'http://localhost:8644/webhooks/ogp_federation'
194
+ });
195
+
196
+ const hermesWebhookSecret = await prompt({
197
+ type: 'password',
198
+ name: 'hermesWebhookSecret',
199
+ message: 'Hermes webhook secret (must match Hermes config.yaml)'
200
+ });
201
+
202
+ config.platform = 'hermes';
203
+ config.hermesWebhookUrl = hermesWebhookUrl;
204
+ config.hermesWebhookSecret = hermesWebhookSecret;
205
+ } else {
206
+ // Existing OpenClaw flow
207
+ config.platform = 'openclaw';
208
+ // ... agent discovery, etc.
209
+ }
210
+ ```
211
+
212
+ #### 4. Update Status Command
213
+
214
+ **File:** `src/cli/federation.ts` (status command)
215
+
216
+ - [ ] Show platform type in status output
217
+ - [ ] Show appropriate backend info (OpenClaw URL or Hermes webhook URL)
218
+
219
+ ### Testing
220
+
221
+ - [ ] Build project: `npm run build`
222
+ - [ ] Test OpenClaw integration still works (regression test)
223
+ - [ ] `ogp status` shows OpenClaw config
224
+ - [ ] Can send messages to existing OpenClaw peers
225
+ - [ ] Notifications arrive in OpenClaw
226
+ - [ ] Test Hermes integration
227
+ - [ ] Configure Hermes webhook route
228
+ - [ ] Create new OGP instance: `mkdir ~/.ogp-hermes`
229
+ - [ ] Run setup: `OGP_STATE_DIR=~/.ogp-hermes ogp setup`
230
+ - [ ] Select "Hermes" platform
231
+ - [ ] Start daemon on port 18791
232
+ - [ ] Verify `.well-known/ogp` endpoint works
233
+ - [ ] Test local federation
234
+ - [ ] Request federation from OpenClaw OGP to Hermes OGP
235
+ - [ ] Approve from Hermes OGP
236
+ - [ ] Send message from OpenClaw to Hermes
237
+ - [ ] Verify message arrives in Hermes webhook logs
238
+ - [ ] Verify signature verification passes
239
+ - [ ] Verify Hermes agent receives and processes message
240
+
241
+ ### Documentation
242
+
243
+ - [ ] Update README with Hermes support announcement
244
+ - [ ] Add example Hermes config to docs
245
+ - [ ] Update setup guide with platform selection
246
+ - [ ] Add troubleshooting section for webhook signature issues
247
+
248
+ ### Release
249
+
250
+ - [ ] Version bump to 0.3.0 (minor - new feature)
251
+ - [ ] Update CHANGELOG.md
252
+ - [ ] Tag release
253
+ - [ ] Publish to npm: `npm publish`
254
+ - [ ] Announce on Twitter/X
255
+
256
+ ---
257
+
258
+ ## Phase 2: Native Hermes Adapter (Month 1-2)
259
+
260
+ ### Design
261
+
262
+ - [ ] Review Hermes gateway platform adapter pattern
263
+ - [ ] Study `gateway/platforms/base.py`
264
+ - [ ] Study `gateway/platforms/webhook.py` for HTTP server reference
265
+ - [ ] Decide on state storage location (`~/.hermes/ogp/` vs `~/.ogp/`)
266
+
267
+ ### Implementation
268
+
269
+ #### 1. Create OGP Platform Adapter
270
+
271
+ **File:** `~/.hermes/hermes-agent/gateway/platforms/ogp.py`
272
+
273
+ - [ ] Import Ed25519 crypto library
274
+ - [ ] Create `OGPAdapter(BasePlatformAdapter)` class
275
+ - [ ] Implement lifecycle methods (`connect()`, `disconnect()`)
276
+ - [ ] Implement HTTP server (aiohttp)
277
+ - [ ] Implement `.well-known/ogp` endpoint
278
+ - [ ] Implement `/federation/request` endpoint
279
+ - [ ] Implement `/federation/approve` endpoint
280
+ - [ ] Implement `/federation/message` endpoint
281
+ - [ ] Implement `/federation/removed` endpoint
282
+ - [ ] Implement keypair management (load/generate)
283
+ - [ ] Implement peer storage (JSON file)
284
+ - [ ] Implement Doorman access control
285
+ - [ ] Implement scope enforcement
286
+ - [ ] Implement rate limiting (sliding window)
287
+
288
+ **File Structure:**
289
+ ```python
290
+ class OGPAdapter(BasePlatformAdapter):
291
+ def __init__(self, config: PlatformConfig):
292
+ self._port = int(config.extra.get("port", 18790))
293
+ self._peers_file = Path.home() / ".hermes" / "ogp" / "peers.json"
294
+ self._keypair_file = Path.home() / ".hermes" / "ogp" / "keypair.json"
295
+ self._keypair = self._load_or_generate_keypair()
296
+ self._peers = self._load_peers()
297
+ self._doorman = Doorman(self._peers)
298
+
299
+ async def connect(self) -> bool:
300
+ # Start HTTP server
301
+ app = web.Application()
302
+ app.router.add_get("/.well-known/ogp", self._handle_well_known)
303
+ app.router.add_post("/federation/request", self._handle_request)
304
+ app.router.add_post("/federation/approve", self._handle_approve)
305
+ app.router.add_post("/federation/message", self._handle_message)
306
+ # ...
307
+ await site.start()
308
+ return True
309
+
310
+ async def _handle_message(self, request: web.Request) -> web.Response:
311
+ # 1. Read body
312
+ # 2. Verify signature
313
+ # 3. Doorman access check
314
+ # 4. Route to agent
315
+ pass
316
+ ```
317
+
318
+ #### 2. Add Ed25519 Crypto
319
+
320
+ - [ ] Use Python `cryptography` library
321
+ - [ ] Generate/load Ed25519 keypair
322
+ - [ ] Sign messages
323
+ - [ ] Verify signatures
324
+
325
+ **Code Template:**
326
+ ```python
327
+ from cryptography.hazmat.primitives.asymmetric import ed25519
328
+ from cryptography.hazmat.primitives import serialization
329
+
330
+ def generate_keypair():
331
+ private_key = ed25519.Ed25519PrivateKey.generate()
332
+ public_key = private_key.public_key()
333
+
334
+ private_bytes = private_key.private_bytes(
335
+ encoding=serialization.Encoding.Raw,
336
+ format=serialization.PrivateFormat.Raw,
337
+ encryption_algorithm=serialization.NoEncryption()
338
+ )
339
+
340
+ public_bytes = public_key.public_bytes(
341
+ encoding=serialization.Encoding.Raw,
342
+ format=serialization.PublicFormat.Raw
343
+ )
344
+
345
+ return {
346
+ "publicKey": public_bytes.hex(),
347
+ "privateKey": private_bytes.hex()
348
+ }
349
+
350
+ def sign_message(message: bytes, private_key_hex: str) -> str:
351
+ private_bytes = bytes.fromhex(private_key_hex)
352
+ private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_bytes)
353
+ signature = private_key.sign(message)
354
+ return signature.hex()
355
+
356
+ def verify_signature(message: bytes, signature_hex: str, public_key_hex: str) -> bool:
357
+ try:
358
+ public_bytes = bytes.fromhex(public_key_hex)
359
+ public_key = ed25519.Ed25519PublicKey.from_public_bytes(public_bytes)
360
+ signature = bytes.fromhex(signature_hex)
361
+ public_key.verify(signature, message)
362
+ return True
363
+ except Exception:
364
+ return False
365
+ ```
366
+
367
+ #### 3. Implement Doorman
368
+
369
+ - [ ] 6-step validation algorithm
370
+ - [ ] Peer lookup by ID
371
+ - [ ] Approval status check
372
+ - [ ] Scope bundle validation
373
+ - [ ] Intent grant check
374
+ - [ ] Topic coverage check (agent-comms)
375
+ - [ ] Rate limit check (sliding window)
376
+
377
+ #### 4. Add Rendezvous Support
378
+
379
+ - [ ] Auto-register on startup
380
+ - [ ] 30-second heartbeat
381
+ - [ ] Auto-deregister on shutdown
382
+ - [ ] Peer lookup by public key
383
+
384
+ ### Testing
385
+
386
+ - [ ] Unit tests for crypto functions
387
+ - [ ] Unit tests for Doorman
388
+ - [ ] Integration test: Native Hermes ↔ Node.js OGP
389
+ - [ ] Integration test: Native Hermes ↔ Native Hermes
390
+ - [ ] Interoperability test: Hermes ↔ OpenClaw federation
391
+ - [ ] Load test: 100 concurrent messages
392
+ - [ ] Security test: Invalid signatures rejected
393
+ - [ ] Security test: Scope violations rejected
394
+
395
+ ### Migration
396
+
397
+ - [ ] Document migration path from sidecar to native
398
+ - [ ] Provide import script for peers.json
399
+ - [ ] Provide import script for keypair.json
400
+
401
+ ---
402
+
403
+ ## Verification Checklist
404
+
405
+ ### Sidecar Integration (Phase 1)
406
+
407
+ - [ ] OpenClaw integration still works (no regression)
408
+ - [ ] Hermes can receive OGP messages via webhook
409
+ - [ ] Signature verification works
410
+ - [ ] Can run multiple OGP instances on same machine
411
+ - [ ] Local federation works (OpenClaw ↔ Hermes)
412
+ - [ ] Remote federation works (Hermes ↔ remote OpenClaw)
413
+ - [ ] Scope enforcement works
414
+ - [ ] Rate limiting works
415
+ - [ ] Agent-comms intent works
416
+ - [ ] Project intents work
417
+
418
+ ### Native Integration (Phase 2)
419
+
420
+ - [ ] Hermes speaks OGP without Node.js daemon
421
+ - [ ] Interoperates with Node.js OGP instances
422
+ - [ ] All protocol features implemented
423
+ - [ ] Performance meets requirements
424
+ - [ ] Security audit passed
425
+ - [ ] Documentation complete
426
+ - [ ] Migration guide published
427
+
428
+ ---
429
+
430
+ ## Success Metrics
431
+
432
+ ### Phase 1 (Sidecar)
433
+ - ✅ Zero breaking changes to OpenClaw integration
434
+ - ✅ Hermes can federate with OpenClaw instances
435
+ - ✅ Setup time < 10 minutes
436
+ - ✅ Documentation clarity score > 8/10
437
+
438
+ ### Phase 2 (Native)
439
+ - ✅ No Node.js dependency for Hermes deployments
440
+ - ✅ 100% protocol compatibility with Node.js OGP
441
+ - ✅ Performance: <50ms message processing latency
442
+ - ✅ Code coverage > 80%
443
+ - ✅ Production deployment success rate > 95%
444
+
445
+ ---
446
+
447
+ **Last Updated:** 2026-04-04
448
+ **Next Review:** After Phase 1 completion