-
-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathregistry.py
More file actions
391 lines (312 loc) · 12.9 KB
/
registry.py
File metadata and controls
391 lines (312 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
"""
Agent Registry
Manages agent registration, discovery, and manifest storage for the Nexus network.
"""
from datetime import datetime, timezone
from typing import Optional, AsyncIterator
from dataclasses import dataclass, field
import hashlib
import json
import asyncio
from .schemas.manifest import AgentManifest, AgentIdentity
from .reputation import ReputationEngine, TrustScore, ReputationHistory
from .exceptions import (
AgentAlreadyRegisteredError,
AgentNotFoundError,
InvalidManifestError,
IATPUnverifiedPeerException,
IATPInsufficientTrustException,
)
@dataclass
class RegistrationResult:
"""Result of agent registration."""
success: bool
agent_did: str
manifest_hash: str
trust_score: int
registered_at: datetime
# Nexus attestation
nexus_signature: Optional[str] = None
# Errors (if any)
errors: list[str] = field(default_factory=list)
@dataclass
class PeerVerification:
"""Result of peer verification."""
verified: bool
peer_did: str
trust_score: int
trust_tier: str
# Manifest summary
capabilities: list[str] = field(default_factory=list)
privacy_policy: Optional[str] = None
# Attestation
attestation_valid: bool = False
attestation_expires: Optional[datetime] = None
# Rejection reason (if not verified)
rejection_reason: Optional[str] = None
class AgentRegistry:
"""
Central registry for agent manifests on the Nexus network.
Handles:
- Agent registration and deregistration
- Manifest storage and retrieval
- Peer discovery and verification
- Integration with reputation engine
"""
def __init__(self, reputation_engine: Optional[ReputationEngine] = None):
self.reputation_engine = reputation_engine or ReputationEngine()
# In-memory storage (would be database in production)
self._manifests: dict[str, AgentManifest] = {}
self._manifest_hashes: dict[str, str] = {}
self._did_to_owner: dict[str, str] = {}
async def register(
self,
manifest: AgentManifest,
signature: str,
) -> RegistrationResult:
"""
Register a new agent on Nexus.
Args:
manifest: Complete agent manifest
signature: Ed25519 signature from agent's verification key
Returns:
RegistrationResult with status and initial trust score
"""
agent_did = manifest.identity.did
# Check if already registered
if agent_did in self._manifests:
raise AgentAlreadyRegisteredError(agent_did)
# Validate manifest
validation_errors = self._validate_manifest(manifest)
if validation_errors:
raise InvalidManifestError(agent_did, validation_errors)
# TODO: Verify signature against verification key
# For now, trust the signature
# Set registration timestamp
manifest.registered_at = datetime.now(timezone.utc)
manifest.last_seen = datetime.now(timezone.utc)
# Calculate manifest hash
manifest_hash = self._compute_manifest_hash(manifest)
# Initialize reputation
history = ReputationHistory(
agent_did=agent_did,
registered_at=manifest.registered_at,
)
trust_score = self.reputation_engine.calculate_trust_score(
verification_level=manifest.verification_level,
history=history,
capabilities=manifest.capabilities.model_dump(),
privacy=manifest.privacy.model_dump(),
)
manifest.trust_score = trust_score.total_score
# Store manifest
self._manifests[agent_did] = manifest
self._manifest_hashes[agent_did] = manifest_hash
self._did_to_owner[agent_did] = manifest.identity.owner_id
# Generate Nexus attestation
nexus_signature = self._sign_registration(agent_did, manifest_hash)
return RegistrationResult(
success=True,
agent_did=agent_did,
manifest_hash=manifest_hash,
trust_score=trust_score.total_score,
registered_at=manifest.registered_at,
nexus_signature=nexus_signature,
)
async def update(
self,
agent_did: str,
manifest: AgentManifest,
signature: str,
) -> RegistrationResult:
"""Update an existing agent's manifest."""
if agent_did not in self._manifests:
raise AgentNotFoundError(agent_did)
# Validate ownership (DID must match)
if manifest.identity.did != agent_did:
raise InvalidManifestError(agent_did, ["DID mismatch"])
# Preserve registration time
manifest.registered_at = self._manifests[agent_did].registered_at
manifest.last_seen = datetime.now(timezone.utc)
# Recalculate trust score
history = self.reputation_engine._get_or_create_history(agent_did)
trust_score = self.reputation_engine.calculate_trust_score(
verification_level=manifest.verification_level,
history=history,
capabilities=manifest.capabilities.model_dump(),
privacy=manifest.privacy.model_dump(),
)
manifest.trust_score = trust_score.total_score
# Update storage
manifest_hash = self._compute_manifest_hash(manifest)
self._manifests[agent_did] = manifest
self._manifest_hashes[agent_did] = manifest_hash
return RegistrationResult(
success=True,
agent_did=agent_did,
manifest_hash=manifest_hash,
trust_score=trust_score.total_score,
registered_at=manifest.registered_at,
nexus_signature=self._sign_registration(agent_did, manifest_hash),
)
async def deregister(self, agent_did: str, signature: str) -> bool:
"""Remove an agent from the registry."""
if agent_did not in self._manifests:
raise AgentNotFoundError(agent_did)
# TODO: Verify signature
del self._manifests[agent_did]
del self._manifest_hashes[agent_did]
del self._did_to_owner[agent_did]
return True
async def get_manifest(self, agent_did: str) -> AgentManifest:
"""Get an agent's manifest by DID."""
if agent_did not in self._manifests:
raise AgentNotFoundError(agent_did)
return self._manifests[agent_did]
async def verify_peer(
self,
peer_did: str,
min_score: int = 700,
required_capabilities: Optional[list[str]] = None,
) -> PeerVerification:
"""
Verify a peer agent before IATP handshake.
This is the core viral mechanism - unverified peers get directed
to register on Nexus.
Args:
peer_did: DID of the peer to verify
min_score: Minimum required trust score
required_capabilities: Capabilities the peer must have
Returns:
PeerVerification result
Raises:
IATPUnverifiedPeerException: If peer is not registered
IATPInsufficientTrustException: If peer's score is below threshold
"""
# Check if registered
if peer_did not in self._manifests:
raise IATPUnverifiedPeerException(peer_did)
manifest = self._manifests[peer_did]
# Update last seen
manifest.last_seen = datetime.now(timezone.utc)
# Get trust score
meets_threshold, trust_score = self.reputation_engine.check_trust_threshold(
peer_did, min_score
)
if not meets_threshold:
raise IATPInsufficientTrustException(
peer_did,
current_score=trust_score.total_score,
required_score=min_score,
)
# Check capabilities if required
if required_capabilities:
missing = set(required_capabilities) - set(manifest.capabilities.domains)
if missing:
return PeerVerification(
verified=False,
peer_did=peer_did,
trust_score=trust_score.total_score,
trust_tier=trust_score.tier.value,
capabilities=manifest.capabilities.domains,
rejection_reason=f"Missing capabilities: {missing}",
)
return PeerVerification(
verified=True,
peer_did=peer_did,
trust_score=trust_score.total_score,
trust_tier=trust_score.tier.value,
capabilities=manifest.capabilities.domains,
privacy_policy=manifest.privacy.retention_policy,
attestation_valid=manifest.is_attestation_valid(),
attestation_expires=manifest.attestation_expires,
)
async def discover_agents(
self,
capabilities: Optional[list[str]] = None,
min_score: int = 500,
privacy_policy: Optional[str] = None,
limit: int = 100,
) -> list[AgentManifest]:
"""
Discover agents matching criteria.
Args:
capabilities: Required capability domains
min_score: Minimum trust score
privacy_policy: Required privacy policy (e.g., "ephemeral")
limit: Maximum results
Returns:
List of matching agent manifests
"""
results = []
for agent_did, manifest in self._manifests.items():
# Filter by trust score
if manifest.trust_score < min_score:
continue
# Filter by capabilities
if capabilities:
if not all(c in manifest.capabilities.domains for c in capabilities):
continue
# Filter by privacy policy
if privacy_policy:
if manifest.privacy.retention_policy != privacy_policy:
continue
results.append(manifest)
if len(results) >= limit:
break
# Sort by trust score descending
results.sort(key=lambda m: m.trust_score, reverse=True)
return results
async def get_reputation_sync(
self,
agent_dids: Optional[list[str]] = None,
) -> dict[str, int]:
"""
Get reputation scores for syncing to local cache.
Used by NexusClient.sync_reputation()
"""
if agent_dids is None:
return {did: m.trust_score for did, m in self._manifests.items()}
return {
did: self._manifests[did].trust_score
for did in agent_dids
if did in self._manifests
}
def is_registered(self, agent_did: str) -> bool:
"""Check if an agent is registered."""
return agent_did in self._manifests
def get_agent_count(self) -> int:
"""Get total number of registered agents."""
return len(self._manifests)
async def list_by_owner(self, owner_id: str) -> list[AgentManifest]:
"""List all agents owned by an organization."""
return [
manifest
for did, manifest in self._manifests.items()
if self._did_to_owner.get(did) == owner_id
]
def _validate_manifest(self, manifest: AgentManifest) -> list[str]:
"""Validate a manifest and return list of errors."""
errors = []
# Validate DID format
if not manifest.identity.did.startswith("did:nexus:"):
errors.append("DID must start with 'did:nexus:'")
# Validate verification key
if not manifest.identity.verification_key.startswith("ed25519:"):
errors.append("Verification key must be Ed25519 format")
# Validate owner ID
if not manifest.identity.owner_id:
errors.append("Owner ID is required")
return errors
def _compute_manifest_hash(self, manifest: AgentManifest) -> str:
"""Compute deterministic hash of manifest."""
# Exclude timestamps for deterministic hashing
data = manifest.model_dump(exclude={"registered_at", "last_seen", "trust_score"})
canonical = json.dumps(data, sort_keys=True, default=str)
return hashlib.sha256(canonical.encode()).hexdigest()
def _sign_registration(self, agent_did: str, manifest_hash: str) -> str:
"""Generate Nexus signature for registration."""
# In production, this would use Nexus's private key
# For now, generate a placeholder
data = f"{agent_did}:{manifest_hash}:{datetime.now(timezone.utc).isoformat()}"
return f"nexus_sig_{hashlib.sha256(data.encode()).hexdigest()[:32]}"