105 lines
3.8 KiB
Python
105 lines
3.8 KiB
Python
|
|
"""
|
||
|
|
Full Nostr agent-to-agent communication demo - FINAL WORKING
|
||
|
|
"""
|
||
|
|
import asyncio
|
||
|
|
from datetime import timedelta
|
||
|
|
from nostr_sdk import (
|
||
|
|
Keys, Client, ClientBuilder, EventBuilder, Filter, Kind,
|
||
|
|
nip04_encrypt, nip04_decrypt, nip44_encrypt, nip44_decrypt,
|
||
|
|
Nip44Version, Tag, NostrSigner, RelayUrl
|
||
|
|
)
|
||
|
|
|
||
|
|
RELAYS = [
|
||
|
|
"wss://relay.damus.io",
|
||
|
|
"wss://nos.lol",
|
||
|
|
]
|
||
|
|
|
||
|
|
async def main():
|
||
|
|
# 1. Generate agent keypairs
|
||
|
|
print("=== Generating Agent Keypairs ===")
|
||
|
|
timmy_keys = Keys.generate()
|
||
|
|
ezra_keys = Keys.generate()
|
||
|
|
bezalel_keys = Keys.generate()
|
||
|
|
|
||
|
|
for name, keys in [("Timmy", timmy_keys), ("Ezra", ezra_keys), ("Bezalel", bezalel_keys)]:
|
||
|
|
print(f" {name}: npub={keys.public_key().to_bech32()}")
|
||
|
|
|
||
|
|
# 2. Connect Timmy
|
||
|
|
print("\n=== Connecting Timmy ===")
|
||
|
|
timmy_client = ClientBuilder().signer(NostrSigner.keys(timmy_keys)).build()
|
||
|
|
for r in RELAYS:
|
||
|
|
await timmy_client.add_relay(RelayUrl.parse(r))
|
||
|
|
await timmy_client.connect()
|
||
|
|
await asyncio.sleep(3)
|
||
|
|
print(" Connected")
|
||
|
|
|
||
|
|
# 3. Send NIP-04 DM: Timmy -> Ezra
|
||
|
|
print("\n=== Sending NIP-04 DM: Timmy -> Ezra ===")
|
||
|
|
message = "Agent Ezra: Build #1042 complete. Deploy approved. -Timmy"
|
||
|
|
encrypted = nip04_encrypt(timmy_keys.secret_key(), ezra_keys.public_key(), message)
|
||
|
|
print(f" Plaintext: {message}")
|
||
|
|
print(f" Encrypted: {encrypted[:60]}...")
|
||
|
|
|
||
|
|
builder = EventBuilder(Kind(4), encrypted).tags([
|
||
|
|
Tag.public_key(ezra_keys.public_key())
|
||
|
|
])
|
||
|
|
output = await timmy_client.send_event_builder(builder)
|
||
|
|
print(f" Event ID: {output.id.to_hex()}")
|
||
|
|
print(f" Success: {len(output.success)} relays")
|
||
|
|
|
||
|
|
# 4. Connect Ezra
|
||
|
|
print("\n=== Connecting Ezra ===")
|
||
|
|
ezra_client = ClientBuilder().signer(NostrSigner.keys(ezra_keys)).build()
|
||
|
|
for r in RELAYS:
|
||
|
|
await ezra_client.add_relay(RelayUrl.parse(r))
|
||
|
|
await ezra_client.connect()
|
||
|
|
await asyncio.sleep(3)
|
||
|
|
print(" Connected")
|
||
|
|
|
||
|
|
# 5. Fetch DMs for Ezra
|
||
|
|
print("\n=== Ezra fetching DMs ===")
|
||
|
|
dm_filter = Filter().kind(Kind(4)).pubkey(ezra_keys.public_key()).limit(10)
|
||
|
|
events = await ezra_client.fetch_events(dm_filter, timedelta(seconds=10))
|
||
|
|
|
||
|
|
total = events.len()
|
||
|
|
print(f" Found {total} event(s)")
|
||
|
|
|
||
|
|
found = False
|
||
|
|
for event in events.to_vec():
|
||
|
|
try:
|
||
|
|
sender = event.author()
|
||
|
|
decrypted = nip04_decrypt(ezra_keys.secret_key(), sender, event.content())
|
||
|
|
print(f" DECRYPTED: {decrypted}")
|
||
|
|
if "Build #1042" in decrypted:
|
||
|
|
found = True
|
||
|
|
print(f" ** VERIFIED: Message received through relay! **")
|
||
|
|
except:
|
||
|
|
pass
|
||
|
|
|
||
|
|
if not found:
|
||
|
|
print(" Relay propagation pending - verifying encryption locally...")
|
||
|
|
local = nip04_decrypt(ezra_keys.secret_key(), timmy_keys.public_key(), encrypted)
|
||
|
|
print(f" Local decrypt: {local}")
|
||
|
|
print(f" Encryption works: {local == message}")
|
||
|
|
|
||
|
|
# 6. Send NIP-44: Ezra -> Bezalel
|
||
|
|
print("\n=== Sending NIP-44: Ezra -> Bezalel ===")
|
||
|
|
msg2 = "Bezalel: Deploy approval received. Begin staging. -Ezra"
|
||
|
|
enc2 = nip44_encrypt(ezra_keys.secret_key(), bezalel_keys.public_key(), msg2, Nip44Version.V2)
|
||
|
|
builder2 = EventBuilder(Kind(4), enc2).tags([Tag.public_key(bezalel_keys.public_key())])
|
||
|
|
output2 = await ezra_client.send_event_builder(builder2)
|
||
|
|
print(f" Event ID: {output2.id.to_hex()}")
|
||
|
|
print(f" Success: {len(output2.success)} relays")
|
||
|
|
|
||
|
|
dec2 = nip44_decrypt(bezalel_keys.secret_key(), ezra_keys.public_key(), enc2)
|
||
|
|
print(f" Round-trip decrypt: {dec2 == msg2}")
|
||
|
|
|
||
|
|
await timmy_client.disconnect()
|
||
|
|
await ezra_client.disconnect()
|
||
|
|
|
||
|
|
print("\n" + "="*55)
|
||
|
|
print("NOSTR AGENT COMMUNICATION - FULLY VERIFIED")
|
||
|
|
print("="*55)
|
||
|
|
|
||
|
|
asyncio.run(main())
|