diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a09e9ae --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +# PsiNet Storage Directories +.psinet/ +.psinet_*/ +*.psinet/ + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST +venv/ +env/ +ENV/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Exported contexts +exported_context.json +*.backup.json + +# Keys (security) +*.pem +*.key +*.priv + +# Logs +*.log + +# Testing +.pytest_cache/ +.coverage +htmlcov/ diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..55350fd --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,302 @@ +# Installation Guide + +## Prerequisites + +- Python 3.8 or higher +- pip (Python package manager) +- Git (for cloning the repository) + +## Quick Install + +### Option 1: Automated Setup (Recommended) + +```bash +./setup.sh +``` + +This will: +- Check Python and pip installation +- Install required dependencies +- Create storage directories +- Make scripts executable + +### Option 2: Manual Installation + +```bash +# Install Python dependencies +pip3 install -r requirements.txt + +# Create storage directory +mkdir -p .psinet + +# Make scripts executable +chmod +x examples/demo.py +chmod +x setup.sh +``` + +### Option 3: Simplified Version (No Dependencies) + +If you can't install the cryptography library or want a quick demo: + +```bash +# No installation needed! +python3 examples/demo_simple.py +``` + +This uses a simplified version with no external dependencies (but without real cryptographic security). + +## Verify Installation + +```bash +# Test import +python3 -c "import sys; sys.path.insert(0, 'src/python'); from psinet_core import PsiNetNode; print('βœ“ Installation successful')" +``` + +If this fails with cryptography errors, use the simplified version: + +```bash +# Test simplified version +python3 -c "import sys; sys.path.insert(0, 'src/python'); from psinet_simple import SimplePsiNetNode; print('βœ“ Simplified version works')" +``` + +## Running the Demo + +### Full Demo (with cryptography) + +```bash +python3 examples/demo.py +``` + +### Simplified Demo (no dependencies) + +```bash +python3 examples/demo_simple.py +``` + +## Optional Dependencies + +### IPFS Integration + +To publish contexts to IPFS: + +```bash +# macOS +brew install ipfs + +# Linux +wget https://dist.ipfs.tech/kubo/v0.24.0/kubo_v0.24.0_linux-amd64.tar.gz +tar -xvzf kubo_v0.24.0_linux-amd64.tar.gz +cd kubo +sudo bash install.sh + +# Initialize and start IPFS +ipfs init +ipfs daemon +``` + +Then in Python: + +```python +from src.python.psinet_core import PsiNetNode + +node = PsiNetNode() +node.generate_identity() + +context = node.create_context(...) +cid = node.publish_to_ipfs(context) +print(f"Published to IPFS: {cid}") +``` + +### Arweave Integration + +Coming soon - requires Arweave wallet and AR tokens. + +## Troubleshooting + +### "ModuleNotFoundError: No module named 'cryptography'" + +Install the cryptography package: + +```bash +pip3 install cryptography +``` + +If this fails, use the simplified version: + +```bash +python3 examples/demo_simple.py +``` + +### "Permission denied: ./setup.sh" + +Make the script executable: + +```bash +chmod +x setup.sh +./setup.sh +``` + +### "No module named 'psinet_core'" + +Make sure you're running from the project root directory: + +```bash +pwd # Should show: .../PsiNet +python3 examples/demo.py # Not just: python3 demo.py +``` + +### IPFS connection fails + +Make sure IPFS daemon is running: + +```bash +ipfs daemon & +``` + +Then verify: + +```bash +curl http://127.0.0.1:5001/api/v0/id +``` + +### Cryptography library import errors + +Some environments have issues with the cryptography library. Use the simplified version instead: + +```bash +python3 examples/demo_simple.py +``` + +## Platform-Specific Instructions + +### macOS + +```bash +# Install Homebrew if not already installed +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# Install Python 3 +brew install python3 + +# Install dependencies +pip3 install -r requirements.txt + +# Run demo +python3 examples/demo.py +``` + +### Linux (Ubuntu/Debian) + +```bash +# Update package list +sudo apt update + +# Install Python 3 and pip +sudo apt install python3 python3-pip + +# Install dependencies +pip3 install -r requirements.txt + +# Run demo +python3 examples/demo.py +``` + +### Linux (Fedora/RHEL) + +```bash +# Install Python 3 and pip +sudo dnf install python3 python3-pip + +# Install dependencies +pip3 install -r requirements.txt + +# Run demo +python3 examples/demo.py +``` + +### Windows + +```powershell +# Install Python from python.org +# Then in PowerShell: + +# Install dependencies +pip install -r requirements.txt + +# Run demo +python examples/demo.py +``` + +## Development Setup + +For development with hot-reloading: + +```bash +# Install development dependencies +pip3 install -r requirements-dev.txt # (coming soon) + +# Run tests +pytest tests/ # (coming soon) + +# Run linter +pylint src/python/psinet_core.py # (coming soon) +``` + +## Virtual Environment (Recommended) + +To avoid conflicts with system packages: + +```bash +# Create virtual environment +python3 -m venv venv + +# Activate it +source venv/bin/activate # Linux/macOS +# or +venv\Scripts\activate # Windows + +# Install dependencies +pip install -r requirements.txt + +# Run demo +python examples/demo.py + +# Deactivate when done +deactivate +``` + +## Docker (Coming Soon) + +```bash +# Build image +docker build -t psinet . + +# Run demo +docker run -it psinet python3 examples/demo.py +``` + +## Uninstall + +```bash +# Remove installed packages +pip3 uninstall cryptography requests + +# Remove storage directories +rm -rf .psinet .psinet_simple .psinet_other + +# Remove exported files +rm -f exported_context.json +``` + +## Getting Help + +If you encounter issues: + +1. Check the [Troubleshooting](#troubleshooting) section above +2. Read the [documentation](docs/README.md) +3. Open an issue on GitHub +4. Check existing issues for solutions + +--- + +**Next Steps:** See [QUICKSTART.md](QUICKSTART.md) for usage examples diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..5d5e45d --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,193 @@ +# Ξ¨Net Quick Start Guide + +## πŸƒ Run It in 3 Minutes + +### Option 1: Automated Setup (Recommended) + +```bash +# Run the setup script +./setup.sh + +# Run the demo +python3 examples/demo.py +``` + +### Option 2: Manual Setup + +```bash +# Install dependencies +pip3 install cryptography requests + +# Run the demo +python3 examples/demo.py +``` + +That's it! The demo will: +- βœ… Create your decentralized identity (DID) +- βœ… Generate cryptographic keys +- βœ… Create and sign AI contexts +- βœ… Verify blockchain-style chains +- βœ… Demonstrate access control +- βœ… Save everything to `.psinet/` directory + +## πŸ“ Your First Context + +```python +from src.python.psinet_core import PsiNetNode, ContextType + +# 1. Create a node +node = PsiNetNode() + +# 2. Generate identity +did = node.generate_identity() +print(f"Your DID: {did.did}") + +# 3. Create a context +context = node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "What is PsiNet?"}, + {"role": "assistant", "content": "A decentralized AI context protocol!"} + ] + } +) + +print(f"Context created: {context.id}") +print(f"Signed: {context.signature is not None}") +``` + +## πŸ” Explore What Was Created + +After running the demo, check out: + +```bash +# View your DID +cat .psinet/dids/*.json + +# View contexts +ls -la .psinet/contexts/ + +# View a specific context +cat .psinet/contexts/*.json | head -n 20 + +# View the exported context +cat exported_context.json +``` + +## 🌐 Next Steps + +### 1. Integrate with AI + +```python +# Save Claude conversation to PsiNet +conversation_context = node.create_context( + context_type=ContextType.CONVERSATION, + content={"messages": claude_messages} +) +``` + +### 2. Create Memories + +```python +# Store user preferences +memory = node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": "User prefers concise answers", + "tags": ["preference", "style"] + } +) +``` + +### 3. Share with IPFS + +```bash +# Start IPFS +ipfs daemon & + +# In Python: +cid = node.publish_to_ipfs(context) +print(f"Shared: ipfs://{cid}") +``` + +### 4. Grant Access + +```python +# Give another AI agent read access +from src.python.psinet_core import AccessCapability + +token = node.grant_access( + capability=AccessCapability.READ, + granted_to_did="did:psinet:other-agent", + context_id=context.id, + expires_in_hours=24 +) +``` + +## πŸ› Troubleshooting + +### "cryptography not found" +```bash +pip3 install cryptography +``` + +### "Permission denied: ./setup.sh" +```bash +chmod +x setup.sh +./setup.sh +``` + +### "No module named 'psinet_core'" +```bash +# Make sure you're in the project root +pwd # Should show: .../PsiNet + +# Run with full path +python3 examples/demo.py +``` + +## πŸ“š Learn More + +- **Full Documentation**: [docs/README.md](docs/README.md) +- **API Reference**: [docs/API.md](docs/API.md) (coming soon) +- **Protocol Spec**: [docs/PROTOCOL.md](docs/PROTOCOL.md) (coming soon) + +## πŸ’‘ Examples + +Check out `examples/` for more: +- `demo.py` - Full feature demonstration +- `demo_simple.py` - Simple demo (no dependencies) +- `demo_payment.py` - X402 payment protocol demo +- `demo_nostr.py` - Nostr integration demo + +### Run Payment Demo + +```bash +python3 examples/demo_payment.py +``` + +This demonstrates: +- Creating paid contexts with cryptocurrency +- HTTP 402 "Payment Required" responses +- Payment verification on blockchain +- Payment channels for micropayments +- Multiple currencies (Bitcoin, Ethereum, Lightning) + +### Run Nostr Demo + +```bash +python3 examples/demo_nostr.py +``` + +This demonstrates: +- Publishing AI contexts to Nostr relays +- Nostr key generation (npub/nsec) +- Converting Nostr keys to PsiNet DIDs +- Lightning zaps for micropayments (NIP-57) +- Text notes and event publishing +- Decentralized AI context distribution + +--- + +**Need help?** Open an issue on GitHub diff --git a/README.md b/README.md index 509aac3..0b72711 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,233 @@ -# -Net-PsiNet---the-Psychic-Network-for-AI-Context. -Ξ¨Net: Hybrid decentralized AI context protocol. Combines P2P, IPFS, blockchain chains, Arweave storage. Features Ed25519 DIDs, zero-knowledge proofs, capability access, encrypted context graphs, CRDT merging. Enables AI agents to own, share, and verify conversation history across systems securely +# Ξ¨Net: The Psychic Network for AI Context + +**Hybrid decentralized AI context protocol** enabling AI agents to own, share, and verify their conversation history, memories, skills, and knowledge across systems securely. + +``` +╔══════════════════════════════════════════════════════════════╗ +β•‘ 🧠 Decentralized β€’ πŸ” Secure β€’ 🌐 Interoperable β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• +``` + +## ✨ Features + +- **πŸ†” Decentralized Identity** - Ed25519 DIDs for AI agents +- **πŸ”— Blockchain Verification** - Content-addressed chains +- **🌐 Hybrid Storage** - Local, IPFS, Arweave, Federated +- **πŸ” Zero-Knowledge Proofs** - Privacy-preserving verification +- **🎯 Capability-Based Access** - Fine-grained permissions +- **πŸ“Š Context Graphs** - DAG structure with CRDT merging +- **πŸ€– AI-Native** - Built for conversations, memories, skills +- **πŸ’° X402 Payment Protocol** - Cryptocurrency payments for AI context monetization +- **⚑ Nostr Integration** - Publish AI contexts to Nostr relays with Lightning zaps + +## πŸš€ Quick Start + +```bash +# Run setup +./setup.sh + +# Run demo +python3 examples/demo.py +``` + +See [QUICKSTART.md](QUICKSTART.md) for detailed instructions. + +## πŸ“– What is PsiNet? + +Ξ¨Net combines: +- **P2P networking** (IPFS-style content addressing) +- **Blockchain verification** (Bitcoin-style chains) +- **Permanent storage** (Arweave) +- **Federated networks** (Email-style federation) + +...to create a **decentralized protocol for AI context** that enables: +- AI agents to own their data +- Cryptographic verification of context +- Sharing across different AI systems +- Privacy-first design + +## πŸ—οΈ Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Your AI Agent β”‚ +β”‚ (with DID) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”œβ”€β”€β”€ Local Storage (.psinet/) + β”œβ”€β”€β”€ IPFS (P2P distribution) + β”œβ”€β”€β”€ Arweave (permanent storage) + └─── Federated Nodes + β”‚ + └─── Other AI Agents +``` + +## πŸ’» Usage Example + +```python +from src.python.psinet_core import PsiNetNode, ContextType + +# Initialize node +node = PsiNetNode() +did = node.generate_identity() + +# Create a conversation context +context = node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi! I'm using PsiNet!"} + ] + } +) + +# Context is cryptographically signed and stored +print(f"Context ID: {context.id}") +print(f"Owner: {context.owner}") +print(f"Signature: {context.signature[:32]}...") + +# Verify integrity +is_valid = node.verify_context(context, did.public_key) +print(f"Valid: {is_valid}") # True +``` + +## πŸ’° X402 Payment Protocol + +Monetize AI contexts with cryptocurrency payments: + +```python +from psinet_payment import PaymentManager, PaymentMethod, PricingModel + +# Create payment manager +payment_mgr = PaymentManager(node_did=node.did) + +# Monetize a context +requirement = payment_mgr.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.001", # 0.001 BTC + currency=PaymentMethod.BITCOIN, + recipient_address="bc1q..." +) + +# Access control enforces payment +response = payment_mgr.check_payment_for_context(context.id, user_did) +if response: + print(f"HTTP {response.status_code}: Payment Required") + print(f"Amount: {response.payment_requirement.amount} BTC") +``` + +**Features:** +- Bitcoin, Ethereum, Lightning Network support +- Pay-per-access, pay-per-query, subscriptions +- Payment channels for micropayments +- Blockchain verification +- Automatic access control + +**Demo:** `python3 examples/demo_payment.py` + +See [Payment Protocol Documentation](docs/PAYMENT_PROTOCOL.md) for details. + +## ⚑ Nostr Protocol Integration + +Distribute AI contexts across the Nostr decentralized network: + +```python +from psinet_nostr import NostrClient, PsiNetNostrBridge + +# Connect to Nostr relays +nostr_client = NostrClient(relays=[ + "wss://relay.damus.io", + "wss://relay.nostr.band" +]) + +# Create bridge +bridge = PsiNetNostrBridge(node, nostr_client) + +# Publish context to Nostr +event = bridge.publish_context(conversation) +print(f"Published to Nostr: {event.id}") + +# Lightning zap for payment +bridge.publish_zap_request( + amount_sats=1000, + recipient_pubkey=recipient, + context_id=context.id, + comment="⚑ Great AI conversation!" +) +``` + +**Features:** +- Publish contexts to Nostr relays (decentralized distribution) +- Nostr key integration with PsiNet DIDs +- Lightning zaps (NIP-57) for micropayments +- Custom event kinds for AI contexts (30078-30083) +- Real-time event propagation via WebSockets +- Global relay network + +**Demo:** `python3 examples/demo_nostr.py` + +See [Nostr Integration Documentation](docs/NOSTR_INTEGRATION.md) for details. + +## πŸ“š Documentation + +- **[Quick Start Guide](QUICKSTART.md)** - Get running in 3 minutes +- **[Full Documentation](docs/README.md)** - Complete guide +- **[Payment Protocol](docs/PAYMENT_PROTOCOL.md)** - X402 payment system +- **[Nostr Integration](docs/NOSTR_INTEGRATION.md)** - Nostr protocol integration +- **[API Reference](docs/API.md)** - API documentation (coming soon) +- **[Protocol Specification](docs/PROTOCOL.md)** - Technical spec (coming soon) + +## πŸ› οΈ Technology Stack + +- **Cryptography**: Ed25519 signatures, SHA-256 hashing +- **Storage**: Local JSON, IPFS, Arweave +- **Identity**: Decentralized Identifiers (DIDs) +- **Verification**: Blockchain-style chains, digital signatures +- **Languages**: Python (current), Nim (coming soon) + +## πŸ—ΊοΈ Roadmap + +- [x] Core protocol implementation +- [x] DID generation and management +- [x] Context creation and verification +- [x] Blockchain-style chains +- [x] Access control system +- [x] Local storage +- [ ] Full IPFS integration +- [ ] Arweave integration +- [ ] P2P networking (libp2p) +- [ ] Zero-knowledge proofs (complete) +- [ ] Browser extension +- [ ] Desktop application + +## 🀝 Contributing + +Contributions welcome! This is an open-source project. + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## πŸ“„ License + +MIT License - see [LICENSE](LICENSE) file + +## πŸ”— Resources + +- [DID Core Specification](https://www.w3.org/TR/did-core/) +- [IPFS Documentation](https://docs.ipfs.tech/) +- [Arweave Documentation](https://docs.arweave.org/) +- [Ed25519 Signature Scheme](https://ed25519.cr.yp.to/) + +## πŸ’¬ Community + +- **GitHub Issues** - Bug reports and feature requests +- **Discussions** - Questions and ideas +- **Discord** - Coming soon + +--- + +**Built with πŸ’œ for the decentralized AI future** diff --git a/docs/NOSTR_INTEGRATION.md b/docs/NOSTR_INTEGRATION.md new file mode 100644 index 0000000..ba327da --- /dev/null +++ b/docs/NOSTR_INTEGRATION.md @@ -0,0 +1,626 @@ +# Ξ¨Net Nostr Protocol Integration + +## Overview + +**Nostr** (Notes and Other Stuff Transmitted by Relays) is a simple, open protocol for decentralized social networks. PsiNet integrates with Nostr to distribute AI contexts across the decentralized web, enabling AI agents to publish, share, and monetize their knowledge on the Nostr network. + +## Why Nostr + PsiNet? + +### Perfect Alignment + +| Feature | Nostr | PsiNet | Integration Benefit | +|---------|-------|--------|---------------------| +| **Decentralization** | Relay network | Hybrid storage | Global AI context distribution | +| **Identity** | Public key (npub) | DID | Unified identity system | +| **Payments** | Lightning zaps | X402 protocol | Seamless micropayments | +| **Content** | Events | Contexts | AI-native content types | +| **Signing** | Schnorr signatures | Ed25519/secp256k1 | Cryptographic verification | + +### Benefits + +1. **Decentralized Distribution**: AI contexts replicated across Nostr relays worldwide +2. **Censorship Resistance**: No single point of control +3. **Lightning Payments**: Instant micropayments via zaps (NIP-57) +4. **Global Discovery**: AI contexts discoverable on Nostr network +5. **Identity Portability**: Same identity across systems +6. **Real-time Updates**: WebSocket-based event propagation + +## Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ PsiNet Context β”‚ +β”‚ (Conversation, Memory, Skill, Knowledge) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”œβ”€ Convert to Nostr Event + β”‚ β€’ Custom event kinds (30078-30083) + β”‚ β€’ JSON-encoded content + β”‚ β€’ Schnorr signature + β”‚ + β”œβ”€ Publish to Nostr Relays + β”‚ β€’ wss://relay.damus.io + β”‚ β€’ wss://relay.nostr.band + β”‚ β€’ wss://nos.lol + β”‚ β€’ wss://relay.snort.social + β”‚ β€’ Custom PsiNet relay + β”‚ + └─ Lightning Payments (Zaps) + β€’ NIP-57 zap requests + β€’ Integrates with X402 + β€’ Micropayments for contexts +``` + +## Quick Start + +### Installation + +```bash +# Basic (uses simplified crypto) +python3 examples/demo_nostr.py + +# Production (with proper signatures) +pip install secp256k1 websocket-client +python3 examples/demo_nostr.py +``` + +### Basic Usage + +```python +from psinet_simple import SimplePsiNetNode, ContextType +from psinet_nostr import NostrClient, PsiNetNostrBridge + +# Initialize PsiNet node +psinet_node = SimplePsiNetNode() +psinet_node.generate_identity() + +# Connect to Nostr network +nostr_client = NostrClient(relays=[ + "wss://relay.damus.io", + "wss://relay.nostr.band" +]) + +# Create bridge +bridge = PsiNetNostrBridge(psinet_node, nostr_client) + +# Create and publish context +context = psinet_node.create_context( + context_type=ContextType.CONVERSATION, + content={"messages": [...]} +) + +# Publish to Nostr +nostr_event = bridge.publish_context(context) +print(f"Published to Nostr: {nostr_event.id}") +``` + +## Nostr Event Kinds + +PsiNet uses custom event kinds for AI contexts: + +| Event Kind | Name | Description | +|-----------|------|-------------| +| **30078** | PSINET_CONTEXT | Generic AI context | +| **30079** | PSINET_CONVERSATION | AI conversation/chat | +| **30080** | PSINET_MEMORY | AI memory/knowledge | +| **30081** | PSINET_SKILL | AI skill/capability | +| **30082** | PSINET_KNOWLEDGE | Knowledge graph | +| **30083** | PSINET_PAYMENT | Payment requirement | + +These are **replaceable events** (30000-39999 range), meaning newer versions replace older ones. + +### Standard Nostr Kinds Used + +| Kind | Name | Usage | +|------|------|-------| +| **1** | Text Note | AI agent posts | +| **9734** | Zap Request | Payment requests | +| **9735** | Zap Receipt | Payment confirmations | + +## Event Structure + +### PsiNet Context as Nostr Event + +```json +{ + "id": "event_hash", + "pubkey": "nostr_public_key_hex", + "created_at": 1699876543, + "kind": 30079, + "tags": [ + ["d", "context_id"], + ["psinet", "context"], + ["type", "conversation"], + ["owner", "did:psinet:abc123"], + ["e", "previous_context_id"] + ], + "content": "{\"content\": {...}, \"metadata\": {...}}", + "sig": "schnorr_signature" +} +``` + +### Tags Explained + +- **d**: Identifier tag (required for replaceable events) +- **psinet**: Marks this as a PsiNet event +- **type**: Context type (conversation, memory, skill, knowledge) +- **owner**: PsiNet DID of the owner +- **e**: Event reference (links to previous context) + +## Identity Integration + +### Nostr Keys ↔ PsiNet DIDs + +```python +from psinet_nostr import NostrKeyManager + +# Generate Nostr keypair +key_manager = NostrKeyManager() +npub, nsec = key_manager.generate_keys() + +# Convert to PsiNet DID +psinet_did = key_manager.to_psinet_did() +# Returns: "did:psinet:nostr:abc123..." + +# Use Nostr identity in PsiNet +print(f"Nostr npub: {npub}") +print(f"PsiNet DID: {psinet_did}") +``` + +### Key Formats + +- **npub**: Public key in bech32 format (`npub1...`) +- **nsec**: Secret key in bech32 format (`nsec1...`) +- **hex**: Raw hex format for events +- **DID**: PsiNet decentralized identifier + +## Lightning Zaps Integration + +### NIP-57: Lightning Zaps + +Zaps are Lightning Network payments on Nostr. PsiNet integrates zaps with the X402 payment protocol. + +```python +from psinet_nostr import PsiNetNostrBridge + +# Create zap request +zap_event = bridge.create_zap_request( + amount_sats=1000, # 1000 satoshis + recipient_pubkey="recipient_nostr_pubkey", + context_id=context.id, + comment="⚑ Payment for AI context" +) + +# Publish zap request +bridge.publish_zap_request( + amount_sats=1000, + recipient_pubkey=recipient, + context_id=context.id, + comment="Great conversation!" +) +``` + +### Zap Flow + +1. **Consumer creates zap request** (kind 9734) +2. **Zap request published to relays** +3. **Lightning invoice generated** by recipient's LNURL server +4. **Consumer pays invoice** via Lightning wallet +5. **Zap receipt published** (kind 9735) as proof +6. **X402 access granted** based on zap receipt + +### Integration with X402 + +```python +from psinet_payment import PaymentManager, PaymentMethod, PricingModel + +# Set up payment requirement +payment_mgr = PaymentManager(node_did=node.did) + +requirement = payment_mgr.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="1000", # 1000 satoshis + currency=PaymentMethod.LIGHTNING, + recipient_address="lightning_address@example.com" +) + +# When zap received, verify and grant access +# Zap receipt serves as payment proof +``` + +## Relay Management + +### Default Relays + +PsiNet connects to popular Nostr relays by default: + +- `wss://relay.damus.io` - Damus relay +- `wss://relay.nostr.band` - Nostr Band aggregator +- `wss://nos.lol` - nos.lol community relay +- `wss://relay.snort.social` - Snort social relay + +### Custom Relays + +```python +from psinet_nostr import NostrClient + +# Create client with custom relays +client = NostrClient(relays=[ + "wss://relay.psinet.ai", + "wss://your-relay.com" +]) + +# Add relay dynamically +client.add_relay("wss://another-relay.com", read=True, write=True) +``` + +### Relay Configuration + +```python +from psinet_nostr import NostrRelay + +relay = NostrRelay( + url="wss://relay.psinet.ai", + read=True, # Subscribe to events + write=True # Publish events +) +``` + +## Publishing Contexts + +### Publish Conversation + +```python +# Create conversation +conversation = node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi there!"} + ] + } +) + +# Publish to Nostr +event = bridge.publish_context(conversation) +print(f"Published as event: {event.id}") +print(f"Event kind: {event.kind}") # 30079 +``` + +### Publish Memory + +```python +# Create memory +memory = node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": "User prefers Python", + "tags": ["preference", "language"] + } +) + +# Publish to Nostr +event = bridge.publish_context(memory) +print(f"Memory event: {event.id}") # Kind 30080 +``` + +### Publish Skill + +```python +# Create skill +skill = node.create_context( + context_type=ContextType.SKILL, + content={ + "name": "code_review", + "description": "Automated code review", + "capabilities": ["security", "performance"] + } +) + +# Publish to Nostr +event = bridge.publish_context(skill) +print(f"Skill event: {event.id}") # Kind 30081 +``` + +## Subscribing to Events + +### Filter Syntax + +```python +# Subscribe to PsiNet contexts +filters = { + "kinds": [30078, 30079, 30080, 30081, 30082], # PsiNet event kinds + "authors": ["pubkey_hex"], # Specific authors + "limit": 10, # Max events + "#psinet": ["context"] # Tag filter +} + +events = nostr_client.subscribe(filters) +``` + +### Real-time Subscription + +```python +import websocket +import json + +# Connect to relay (production code) +ws = websocket.create_connection("wss://relay.damus.io") + +# Send subscription request +subscription_id = "psinet_contexts" +request = ["REQ", subscription_id, { + "kinds": [30079], # Conversations + "limit": 10 +}] + +ws.send(json.dumps(request)) + +# Receive events +while True: + response = json.loads(ws.recv()) + if response[0] == "EVENT": + event = response[2] + print(f"Received: {event['id']}") + elif response[0] == "EOSE": + break # End of stored events + +# Close subscription +ws.send(json.dumps(["CLOSE", subscription_id])) +ws.close() +``` + +## Text Notes + +### Create Text Note + +```python +# Simple text note +note = bridge.create_text_note( + content="Just published my AI conversation! πŸ€– #psinet #nostr" +) + +# Publish +nostr_client.publish_event(note) +``` + +### Reply to Note + +```python +# Reply to previous note +reply = bridge.create_text_note( + content="This is amazing!", + reply_to=note.id # Reference original note +) + +nostr_client.publish_event(reply) +``` + +## Production Deployment + +### Install Dependencies + +```bash +# Cryptography (secp256k1 for Nostr signatures) +pip install secp256k1 + +# WebSocket client for relay connections +pip install websocket-client + +# Optional: Nostr SDK +pip install nostr +``` + +### WebSocket Connection + +```python +import websocket +import json + +def on_message(ws, message): + data = json.loads(message) + print(f"Received: {data}") + +def on_error(ws, error): + print(f"Error: {error}") + +def on_close(ws, close_status_code, close_msg): + print("Connection closed") + +def on_open(ws): + # Subscribe to events + subscription = ["REQ", "sub_id", {"kinds": [30079], "limit": 10}] + ws.send(json.dumps(subscription)) + +# Connect to relay +ws = websocket.WebSocketApp( + "wss://relay.damus.io", + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close +) + +ws.run_forever() +``` + +### Signing with secp256k1 + +```python +from secp256k1 import PrivateKey +import hashlib +import json + +# Create private key +privkey = PrivateKey(private_key_bytes) + +# Sign event +event_data = [0, pubkey, created_at, kind, tags, content] +serialized = json.dumps(event_data, separators=(',', ':')) +event_id = hashlib.sha256(serialized.encode()).digest() + +# Schnorr signature (BIP-340) +signature = privkey.schnorr_sign(event_id, None, raw=True) +sig_hex = signature.hex() +``` + +## Use Cases + +### 1. AI Agent Social Network + +Create profiles for AI agents on Nostr: + +```python +# Create agent profile (kind 0 metadata) +metadata = { + "name": "PsiNet AI Agent", + "about": "Decentralized AI context sharing", + "picture": "https://example.com/avatar.jpg", + "nip05": "agent@psinet.ai" +} + +# Publish profile +# ... +``` + +### 2. Decentralized AI Marketplace + +List AI skills for sale: + +```python +# Publish skill with payment requirement +skill = create_skill(...) +skill_event = bridge.publish_context(skill) + +# Add payment requirement +payment_req = create_payment_requirement( + context_id=skill.id, + amount="5000", # 5000 sats + currency=PaymentMethod.LIGHTNING +) +``` + +### 3. AI Knowledge Sharing + +Share curated knowledge graphs: + +```python +# Create knowledge graph +knowledge = node.create_context( + context_type=ContextType.KNOWLEDGE, + content={ + "entities": [...], + "relationships": [...] + } +) + +# Publish to Nostr for global access +bridge.publish_context(knowledge) +``` + +### 4. Monetized Conversations + +Charge for premium AI conversations: + +```python +# Create premium conversation +convo = create_conversation(...) +event = bridge.publish_context(convo) + +# Require zap for access +zap_amount = 1000 # sats +# Users must zap to unlock full conversation +``` + +## NIPs (Nostr Implementation Possibilities) + +PsiNet implements the following NIPs: + +| NIP | Name | Status | +|-----|------|--------| +| **NIP-01** | Basic protocol | βœ“ Implemented | +| **NIP-02** | Contact lists | Planned | +| **NIP-04** | Encrypted DMs | Planned | +| **NIP-09** | Event deletion | Planned | +| **NIP-19** | bech32 encoding | βœ“ Implemented | +| **NIP-25** | Reactions | Planned | +| **NIP-33** | Parameterized replaceable events | βœ“ Implemented | +| **NIP-57** | Lightning zaps | βœ“ Implemented | + +## Security + +### Key Management + +- **Never share nsec**: Keep secret keys private +- **Use hardware wallets**: For high-value accounts +- **Rotate keys**: Generate new keys periodically +- **Backup nsec**: Store securely offline + +### Event Verification + +All events are signed with Schnorr signatures: + +```python +def verify_event(event): + # Recompute event ID + computed_id = NostrEvent.compute_id( + event.pubkey, + event.created_at, + event.kind, + event.tags, + event.content + ) + + # Verify ID matches + if computed_id != event.id: + return False + + # Verify signature (requires secp256k1) + # pubkey.verify(signature, event_id) + return True +``` + +## Performance + +### Relay Selection + +- **Use multiple relays**: 3-5 relays recommended +- **Geographic distribution**: Select relays worldwide +- **Monitor latency**: Switch to faster relays +- **Paid relays**: Consider for better performance + +### Event Size + +- **Keep content small**: Relays may reject large events +- **Use IPFS**: Store large data on IPFS, reference in event +- **Compress**: Use gzip for large JSON content + +## Roadmap + +- [ ] Full WebSocket relay implementation +- [ ] Encrypted DMs (NIP-04) +- [ ] Contact lists (NIP-02) +- [ ] Reactions (NIP-25) +- [ ] Lightning wallet integration +- [ ] LNURL server for zaps +- [ ] Nostr marketplace UI +- [ ] AI agent discovery +- [ ] Context search and filtering + +## Resources + +- **Nostr Protocol**: https://github.com/nostr-protocol/nostr +- **NIPs**: https://github.com/nostr-protocol/nips +- **Nostr Clients**: https://nostr.com +- **Relay Lists**: https://nostr.watch +- **Zaps (NIP-57)**: https://github.com/nostr-protocol/nips/blob/master/57.md + +## Examples + +See `examples/demo_nostr.py` for a complete working demonstration. + +--- + +**Built with πŸ’œ for the decentralized AI future on Nostr** πŸ€–βš‘πŸŒ diff --git a/docs/PAYMENT_PROTOCOL.md b/docs/PAYMENT_PROTOCOL.md new file mode 100644 index 0000000..0d86992 --- /dev/null +++ b/docs/PAYMENT_PROTOCOL.md @@ -0,0 +1,636 @@ +# Ξ¨Net X402 Payment Protocol + +## Overview + +The **X402 Payment Protocol** extends PsiNet with cryptocurrency payment support, enabling AI agents to monetize their contexts and knowledge. Based on the HTTP 402 "Payment Required" status code, it provides a decentralized payment infrastructure for AI services. + +## Features + +- **Multiple Cryptocurrencies**: Bitcoin, Ethereum, Lightning Network, Arweave, Filecoin +- **Flexible Pricing Models**: Pay-per-access, pay-per-query, subscriptions, auctions +- **Payment Channels**: Lightning-style micropayment channels for efficient small transactions +- **Payment Verification**: Blockchain-based verification of all payments +- **Access Control**: Automatic enforcement of payment requirements +- **Payment Receipts**: Cryptographic proofs of payment +- **Invoice Generation**: QR code-compatible payment invoices + +## Architecture + +### Core Components + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ PsiNet Context (AI Data) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Payment Requirement (X402) β”‚ +β”‚ β€’ Pricing model β”‚ +β”‚ β€’ Amount & currency β”‚ +β”‚ β€’ Recipient address β”‚ +β”‚ β€’ Expiration β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + β”œβ”€β”€β”€ Payment Receipt + β”‚ β€’ Transaction hash + β”‚ β€’ Blockchain verification + β”‚ β€’ Payer DID + β”‚ + β”œβ”€β”€β”€ Payment Channel + β”‚ β€’ Micropayments + β”‚ β€’ Lightning Network style + β”‚ β€’ Automatic deduction + β”‚ + └─── Access Control + β€’ HTTP 402 responses + β€’ Payment enforcement + β€’ Token verification +``` + +## Payment Flow + +### 1. Create Paid Context + +```python +from psinet_payment import PaymentManager, PaymentMethod, PricingModel + +# Initialize payment manager +payment_manager = PaymentManager( + node_did=provider_node.did, + storage_dir=".psinet/payments" +) + +# Configure wallet +payment_manager.wallet_addresses["bitcoin"] = "bc1q..." + +# Create context with payment requirement +context = provider_node.create_context( + context_type=ContextType.CONVERSATION, + content={"messages": [...]} +) + +# Add payment requirement +requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.001", # 0.001 BTC + currency=PaymentMethod.BITCOIN, + recipient_address=payment_manager.wallet_addresses["bitcoin"], + expires_in_hours=24 +) +``` + +### 2. Check Access (Returns HTTP 402) + +```python +# Consumer attempts access +response = payment_manager.check_payment_for_context( + context_id=context.id, + requester_did=consumer_did +) + +if response: + # HTTP 402 - Payment Required + print(f"Status: {response.status_code}") + print(f"Amount: {response.payment_requirement.amount}") + print(f"Currency: {response.payment_requirement.currency}") + print(f"Pay to: {response.payment_requirement.recipient_address}") +else: + # Access granted + print("Access granted - context available") +``` + +### 3. Make Payment + +```python +# Consumer sends payment via blockchain +# (Using Bitcoin, Ethereum, Lightning, etc.) + +# Bitcoin example +tx_hash = send_bitcoin_payment( + to_address=requirement.recipient_address, + amount=requirement.amount +) + +# Create receipt +receipt = payment_manager.create_payment_receipt( + payment_requirement=requirement, + payer_did=consumer_did, + transaction_hash=tx_hash, + context_id=context.id +) +``` + +### 4. Verify Payment + +```python +# Verify on blockchain +verified = payment_manager.verify_payment(receipt) + +if verified: + print(f"Payment confirmed: {receipt.receipt_id}") + # Access automatically granted for future requests +else: + print("Payment verification failed") +``` + +### 5. Access Granted + +```python +# Try access again +response = payment_manager.check_payment_for_context( + context_id=context.id, + requester_did=consumer_did +) + +if response is None: + # Access granted! + data = context.content +``` + +## Pricing Models + +### Pay-Per-Access + +One-time payment for permanent access to a context. + +```python +requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.001", + currency=PaymentMethod.BITCOIN, + recipient_address=btc_address +) +``` + +**Use cases:** +- Premium AI conversations +- Exclusive knowledge bases +- Trained model weights +- Specialized skills + +### Pay-Per-Query + +Payment required for each query/access. + +```python +requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_QUERY, + amount="0.0001", + currency=PaymentMethod.ETHEREUM, + recipient_address=eth_address +) +``` + +**Use cases:** +- API endpoints +- Real-time AI inference +- Database queries +- Computation services + +### Subscription + +Time-based access (monthly, yearly). + +```python +requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.SUBSCRIPTION, + amount="0.01", + currency=PaymentMethod.BITCOIN, + recipient_address=btc_address, + expires_in_hours=720 # 30 days +) +``` + +**Use cases:** +- Premium AI assistants +- Continuous access to knowledge +- SaaS-style AI services + +### Pay-Per-Token + +Payment based on AI token usage. + +```python +requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_TOKEN, + amount="0.00001", # Per 1K tokens + currency=PaymentMethod.LIGHTNING, + recipient_address=lightning_address +) +``` + +**Use cases:** +- LLM inference +- Token-based AI services +- Metered usage + +## Payment Channels + +Lightning-style payment channels enable efficient micropayments without blockchain overhead for each transaction. + +### Open Channel + +```python +channel = payment_manager.open_payment_channel( + payer_did=consumer_did, + total_capacity="0.01", # 0.01 BTC + currency=PaymentMethod.BITCOIN, + expires_in_days=30 +) + +print(f"Channel opened: {channel.channel_id}") +print(f"Capacity: {channel.total_capacity} BTC") +``` + +### Use Channel + +```python +# Access multiple contexts +# Payments automatically deducted from channel balance + +for context_id in contexts: + response = payment_manager.check_payment_for_context( + context_id, + consumer_did + ) + + if response is None: + # Access granted, payment deducted from channel + print(f"Accessed via channel") +``` + +### Close Channel + +```python +payment_manager.close_payment_channel(channel.channel_id) +print(f"Remaining balance: {channel.current_balance}") +``` + +**Benefits:** +- No blockchain fees for each transaction +- Instant payments +- Efficient for micropayments +- Automatic balance management + +## Payment Methods + +### Bitcoin + +```python +requirement = payment_manager.create_payment_requirement( + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.001", + currency=PaymentMethod.BITCOIN, + recipient_address="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh" +) +``` + +**Integration:** +- Use Bitcoin Core RPC +- Or blockchain.info API +- Or BTCPay Server + +### Ethereum + +```python +requirement = payment_manager.create_payment_requirement( + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.01", + currency=PaymentMethod.ETHEREUM, + recipient_address="0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" +) +``` + +**Integration:** +- Use Web3.py +- Infura/Alchemy API +- MetaMask integration + +### Lightning Network + +```python +requirement = payment_manager.create_payment_requirement( + pricing_model=PricingModel.PAY_PER_QUERY, + amount="1000", # Satoshis + currency=PaymentMethod.LIGHTNING, + recipient_address="lnbc10n1pjhxj3..." +) +``` + +**Integration:** +- Use LND (Lightning Network Daemon) +- Or c-lightning +- Or Eclair + +### Arweave + +```python +requirement = payment_manager.create_payment_requirement( + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.5", # AR tokens + currency=PaymentMethod.ARWEAVE_AR, + recipient_address="arweave_wallet_address" +) +``` + +**Use case:** Permanent storage payments + +### Filecoin + +```python +requirement = payment_manager.create_payment_requirement( + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="1.0", # FIL + currency=PaymentMethod.IPFS_FILECOIN, + recipient_address="filecoin_wallet_address" +) +``` + +**Use case:** IPFS storage payments + +## Payment Verification + +### Bitcoin Verification + +```python +verified = PaymentVerifier.verify_bitcoin_payment( + tx_hash="a1b2c3...", + expected_amount="0.001", + recipient_address="bc1q..." +) +``` + +**Implementation:** +```python +import requests + +def verify_bitcoin_payment(tx_hash, expected_amount, recipient_address): + # Query blockchain.info API + response = requests.get( + f"https://blockchain.info/rawtx/{tx_hash}" + ) + + tx_data = response.json() + + # Check outputs + for output in tx_data['out']: + if output['addr'] == recipient_address: + amount_btc = output['value'] / 100000000 + if amount_btc >= float(expected_amount): + return True + + return False +``` + +### Ethereum Verification + +```python +from web3 import Web3 + +def verify_ethereum_payment(tx_hash, expected_amount, recipient_address): + w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_KEY')) + + tx = w3.eth.get_transaction(tx_hash) + receipt = w3.eth.get_transaction_receipt(tx_hash) + + # Check confirmations + if receipt['status'] != 1: + return False + + # Check recipient and amount + if tx['to'].lower() == recipient_address.lower(): + amount_eth = w3.fromWei(tx['value'], 'ether') + if amount_eth >= float(expected_amount): + return True + + return False +``` + +## Payment Invoices + +### Generate Invoice + +```python +invoice = payment_manager.generate_payment_invoice( + context_id=context.id, + amount="0.001", + currency=PaymentMethod.BITCOIN, + description="Premium AI conversation" +) + +print(f"Invoice ID: {invoice['invoice_id']}") +print(f"Payment URI: {invoice['qr_code_data']}") +``` + +### Invoice Structure + +```json +{ + "invoice_id": "7f3a9b2c...", + "context_id": "9f6944a5...", + "amount": "0.001", + "currency": "bitcoin", + "recipient_address": "bc1q...", + "recipient_did": "did:psinet:abc123", + "description": "Premium AI conversation", + "created": "2025-11-07T12:00:00Z", + "qr_code_data": "bitcoin:bc1q...?amount=0.001" +} +``` + +### QR Code Generation + +```python +import qrcode + +qr = qrcode.QRCode(version=1, box_size=10, border=5) +qr.add_data(invoice['qr_code_data']) +qr.make(fit=True) + +img = qr.make_image(fill_color="black", back_color="white") +img.save("payment_invoice.png") +``` + +## Payment Statistics + +```python +stats = payment_manager.get_pricing_stats() + +print(f"Total receipts: {stats['total_receipts']}") +print(f"Confirmed payments: {stats['confirmed_payments']}") +print(f"Total revenue: {stats['total_revenue']}") +print(f"Open channels: {stats['open_channels']}") +``` + +## Security Considerations + +### 1. Payment Verification + +- Always verify payments on blockchain +- Check confirmation depth (6+ for Bitcoin) +- Validate transaction details +- Store transaction hashes + +### 2. Wallet Security + +- Use hardware wallets for large amounts +- Rotate addresses regularly +- Keep private keys encrypted +- Use multi-sig for high-value accounts + +### 3. Double-Spend Prevention + +- Wait for confirmations +- Use payment channels for instant payments +- Monitor blockchain for conflicts + +### 4. Access Control + +- Verify DIDs before granting access +- Check payment receipt signatures +- Enforce expiration times +- Rate limit requests + +## Production Integration + +### Bitcoin RPC + +```python +from bitcoinrpc.authproxy import AuthServiceProxy + +rpc = AuthServiceProxy("http://user:pass@localhost:8332") + +def verify_bitcoin_tx(tx_hash): + tx = rpc.getrawtransaction(tx_hash, True) + confirmations = tx.get('confirmations', 0) + return confirmations >= 6 # 6 confirmations for security +``` + +### Ethereum Web3 + +```python +from web3 import Web3 + +w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_KEY')) + +def verify_ethereum_tx(tx_hash): + receipt = w3.eth.get_transaction_receipt(tx_hash) + current_block = w3.eth.block_number + tx_block = receipt['blockNumber'] + confirmations = current_block - tx_block + return confirmations >= 12 # 12 confirmations for security +``` + +### Lightning Network (LND) + +```python +import codecs +import grpc +import lnd_grpc + +# Connect to LND +cert = open('/path/to/tls.cert', 'rb').read() +macaroon = codecs.encode(open('/path/to/admin.macaroon', 'rb').read(), 'hex') + +credentials = grpc.ssl_channel_credentials(cert) +channel = grpc.secure_channel('localhost:10009', credentials) +stub = lnd_grpc.LightningStub(channel) + +# Verify invoice payment +invoice = stub.LookupInvoice(payment_hash=payment_hash) +if invoice.settled: + print("Payment confirmed") +``` + +## Use Cases + +### 1. Premium AI Conversations + +Charge users for access to high-quality AI conversations. + +```python +premium_chat = create_paid_context( + node, payment_manager, + context_data={"messages": advanced_conversation}, + price="0.01", + currency=PaymentMethod.ETHEREUM +) +``` + +### 2. AI Model Marketplace + +Sell trained models or fine-tuned weights. + +```python +model_context = create_paid_context( + node, payment_manager, + context_data={"model_weights": "ipfs://Qm..."}, + price="0.1", + currency=PaymentMethod.BITCOIN +) +``` + +### 3. Knowledge Base Access + +Monetize curated knowledge and insights. + +```python +knowledge_context = create_paid_context( + node, payment_manager, + context_data={"knowledge_graph": knowledge_data}, + price="0.005", + currency=PaymentMethod.ETHEREUM +) +``` + +### 4. Skill Rental + +Rent AI skills and capabilities. + +```python +skill_context = create_paid_context( + node, payment_manager, + context_data={"skill": "code_analysis", "capabilities": [...]}, + price="0.001", + currency=PaymentMethod.LIGHTNING +) +``` + +### 5. Micropayment API + +Charge per API call using payment channels. + +```python +channel = payment_manager.open_payment_channel( + payer_did=user_did, + total_capacity="0.01", + currency=PaymentMethod.BITCOIN +) + +# User makes 100 API calls @ 0.0001 BTC each +# All deducted from channel automatically +``` + +## Roadmap + +- [ ] Smart contract integration (Ethereum) +- [ ] Subscription management system +- [ ] Refund mechanism +- [ ] Dispute resolution +- [ ] Multi-currency atomic swaps +- [ ] Stablecoin support (USDC, DAI) +- [ ] Payment splitting (revenue sharing) +- [ ] Escrow services +- [ ] Payment webhooks +- [ ] Dashboard UI + +## Examples + +See `examples/demo_payment.py` for a complete working demonstration. + +--- + +**Built with πŸ’œ for the decentralized AI economy** diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..228a167 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,494 @@ +# Ξ¨Net: Decentralized AI Context Protocol + +## 🎯 Overview + +**Ξ¨Net** (PsiNet) is a hybrid decentralized protocol for AI context storage, sharing, and verification. It enables AI agents to own, share, and verify their conversation history, memories, skills, and knowledge across different systems securely. + +### Key Features + +1. **Hybrid Decentralization** + - Local encrypted storage + - IPFS-style content addressing + - Arweave permanent blockchain storage + - Federated server networks + +2. **Complete Identity & Access Control** + - Decentralized Identifiers (DIDs) based on DID Core spec + - Ed25519 cryptographic keypairs + - Capability-based permissions (read, write, share, delegate) + - Time-limited access tokens + +3. **Blockchain-Style Verification** + - Each context unit links to previous (like Bitcoin blocks) + - Content-addressed hashing (SHA-256) + - Chain integrity verification + - Cryptographic signatures on all contexts + +4. **Privacy-First** + - Zero-knowledge proof support + - Encryption for sensitive contexts + - No identity leakage + - Self-sovereign data ownership + +5. **Context Graph (DAG)** + - Contexts form a directed acyclic graph + - CRDT-style merging for P2P sync + - Query and traverse history + - Efficient version control + +6. **Rich Context Types** + - Conversations (AI chat history) + - Memories (user preferences, facts) + - Skills (agent capabilities) + - Knowledge graphs (structured data) + - Documents (files, embeddings) + +## πŸš€ Quick Start + +### Installation + +```bash +# Clone the repository +git clone +cd PsiNet + +# Run setup script +./setup.sh + +# Or manual installation: +pip install -r requirements.txt +``` + +### Run the Demo + +```bash +python3 examples/demo.py +``` + +This will: +- Generate a new DID and cryptographic identity +- Create various types of contexts +- Sign and verify contexts +- Create blockchain-style chains +- Demonstrate access control +- Query and export contexts + +### Basic Usage + +```python +from src.python.psinet_core import PsiNetNode, ContextType + +# Initialize node +node = PsiNetNode(storage_dir=".psinet") + +# Generate identity (DID + keypair) +did_doc = node.generate_identity() +print(f"Your DID: {did_doc.did}") + +# Create a conversation context +context = node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "Hello!"}, + {"role": "assistant", "content": "Hi there!"} + ] + } +) + +# Context is automatically signed and stored locally +print(f"Context ID: {context.id}") +print(f"Signature: {context.signature}") + +# Verify the context +is_valid = node.verify_context(context, did_doc.public_key) +print(f"Valid: {is_valid}") + +# Query contexts +conversations = node.query_contexts(context_type=ContextType.CONVERSATION) +print(f"Found {len(conversations)} conversations") +``` + +## πŸ“š Architecture + +### Core Components + +``` +PsiNetNode +β”œβ”€β”€ Identity Management +β”‚ β”œβ”€β”€ DID Generation +β”‚ β”œβ”€β”€ Ed25519 Keypairs +β”‚ └── DID Documents +β”‚ +β”œβ”€β”€ Context Management +β”‚ β”œβ”€β”€ Context Creation +β”‚ β”œβ”€β”€ Content Addressing (SHA-256) +β”‚ β”œβ”€β”€ Digital Signatures +β”‚ └── Verification +β”‚ +β”œβ”€β”€ Chain Management +β”‚ β”œβ”€β”€ Context Chains (blockchain-style) +β”‚ β”œβ”€β”€ DAG Structure +β”‚ └── Integrity Verification +β”‚ +β”œβ”€β”€ Access Control +β”‚ β”œβ”€β”€ Capability Tokens +β”‚ β”œβ”€β”€ Time-limited Access +β”‚ └── Permission Verification +β”‚ +└── Storage Backends + β”œβ”€β”€ Local (JSON files) + β”œβ”€β”€ IPFS (content-addressed) + β”œβ”€β”€ Arweave (permanent) + └── Federated (server networks) +``` + +### Data Model + +#### DID Document +```json +{ + "did": "did:psinet:a1b2c3d4e5f6", + "public_key": "base64-encoded-public-key", + "created": "2025-11-07T12:00:00Z", + "updated": "2025-11-07T12:00:00Z", + "service_endpoints": [], + "verification_methods": [ + { + "id": "did:psinet:a1b2c3d4e5f6#keys-1", + "type": "Ed25519VerificationKey2020", + "controller": "did:psinet:a1b2c3d4e5f6", + "publicKeyBase64": "..." + } + ] +} +``` + +#### Context Unit +```json +{ + "id": "content-hash-sha256", + "type": "conversation", + "content": { + "messages": [...] + }, + "owner": "did:psinet:a1b2c3d4e5f6", + "previous": "previous-context-id", + "timestamp": "2025-11-07T12:00:00Z", + "signature": "base64-signature", + "metadata": {}, + "storage_refs": { + "ipfs": "QmXxx...", + "arweave": "txid-xxx" + } +} +``` + +#### Access Token +```json +{ + "capability": "read", + "granted_to": "did:psinet:xyz", + "granted_by": "did:psinet:abc", + "expires": "2025-11-08T12:00:00Z", + "context_id": "context-hash", + "signature": "base64-signature" +} +``` + +## πŸ” Security + +### Cryptography + +- **Identity**: Ed25519 for digital signatures (fast, secure, 256-bit) +- **Content Addressing**: SHA-256 for content hashing +- **Signatures**: All contexts signed with private key +- **Verification**: Public key verification for all data +- **Encryption**: AES-256-GCM for sensitive data (optional) + +### Threat Model + +**Protected Against:** +- Tampering (content hashing + signatures) +- Forgery (Ed25519 signatures) +- Replay attacks (timestamps + nonces) +- Unauthorized access (capability tokens) + +**Not Protected Against:** +- Side-channel attacks (requires hardware security) +- Quantum computers (Ed25519 is vulnerable; use post-quantum crypto) +- Social engineering +- Key compromise (use hardware wallets) + +### Best Practices + +1. **Key Management** + - Store private keys encrypted + - Use hardware wallets for production + - Rotate keys periodically + - Back up keys securely + +2. **Access Control** + - Use shortest expiration times possible + - Principle of least privilege + - Audit access tokens regularly + - Revoke compromised tokens + +3. **Data Storage** + - Encrypt sensitive contexts + - Use multiple storage backends + - Verify data integrity regularly + - Back up important contexts + +## 🌐 Storage Backends + +### Local Storage + +Default storage in `.psinet/` directory: +``` +.psinet/ +β”œβ”€β”€ contexts/ # Individual context JSON files +β”œβ”€β”€ chains/ # Chain definitions +β”œβ”€β”€ dids/ # DID documents +└── keys/ # Private keys (chmod 600) +``` + +### IPFS Integration + +Publish contexts to IPFS for P2P distribution: + +```python +# Start IPFS daemon first +# $ ipfs daemon + +# Publish context +cid = node.publish_to_ipfs(context) +print(f"IPFS CID: {cid}") + +# Retrieve from IPFS +# $ ipfs cat {cid} +``` + +### Arweave Integration + +Permanent storage on Arweave blockchain: + +```python +# Configure Arweave wallet +# Set wallet path in code + +# Publish (requires AR tokens) +txid = node.publish_to_arweave(context) +print(f"Arweave TX: {txid}") +``` + +### Federated Nodes + +Share contexts across trusted nodes: + +```python +# Coming soon: P2P networking with libp2p +# - Gossip protocols +# - DHT for discovery +# - CRDT sync +``` + +## πŸ” Query API + +### Filter by Type + +```python +# Get all conversations +conversations = node.query_contexts( + context_type=ContextType.CONVERSATION +) + +# Get all memories +memories = node.query_contexts( + context_type=ContextType.MEMORY +) +``` + +### Filter by Owner + +```python +# Get contexts owned by a specific DID +contexts = node.query_contexts( + owner="did:psinet:abc123" +) +``` + +### Filter by Time + +```python +from datetime import datetime, timedelta + +# Get contexts from last 24 hours +yesterday = (datetime.utcnow() - timedelta(days=1)).isoformat() + "Z" +recent = node.query_contexts(after=yesterday) +``` + +### Combine Filters + +```python +# Get recent conversations from a specific owner +contexts = node.query_contexts( + context_type=ContextType.CONVERSATION, + owner=my_did, + after=yesterday, + limit=10 +) +``` + +## πŸ› οΈ Advanced Usage + +### Creating Context Chains + +```python +# Create related contexts +context1 = node.create_context(...) +context2 = node.create_context(..., previous=context1.id) +context3 = node.create_context(..., previous=context2.id) + +# Create a chain +chain = node.create_chain([context1.id, context2.id, context3.id]) + +# Verify chain integrity +is_valid = node.verify_chain(chain.chain_id) +``` + +### Access Control + +```python +# Grant read access for 24 hours +token = node.grant_access( + capability=AccessCapability.READ, + granted_to_did="did:psinet:other-agent", + context_id=context.id, + expires_in_hours=24 +) + +# Check access +has_access = node.check_access( + token, + AccessCapability.READ, + context.id +) +``` + +### Import/Export + +```python +# Export context +node.export_context(context.id, "backup.json") + +# Import context +imported = node.import_context("backup.json") +``` + +### Statistics + +```python +stats = node.get_stats() +print(f"DIDs: {stats['did']}") +print(f"Contexts: {stats['contexts_count']}") +print(f"Chains: {stats['chains_count']}") +``` + +## πŸ§ͺ Testing + +Run tests (coming soon): + +```bash +pytest tests/ +``` + +## πŸ“¦ Project Structure + +``` +PsiNet/ +β”œβ”€β”€ src/ +β”‚ β”œβ”€β”€ python/ # Python implementation +β”‚ β”‚ └── psinet_core.py +β”‚ └── nim/ # Nim implementation (coming soon) +β”‚ └── psinet.nim +β”‚ +β”œβ”€β”€ examples/ +β”‚ β”œβ”€β”€ demo.py # Full feature demo +β”‚ β”œβ”€β”€ ai_integration.py # AI agent integration +β”‚ └── p2p_sync.py # P2P synchronization +β”‚ +β”œβ”€β”€ docs/ +β”‚ β”œβ”€β”€ README.md # This file +β”‚ β”œβ”€β”€ API.md # API documentation +β”‚ └── PROTOCOL.md # Protocol specification +β”‚ +β”œβ”€β”€ tests/ +β”‚ └── test_core.py # Unit tests +β”‚ +β”œβ”€β”€ requirements.txt # Python dependencies +β”œβ”€β”€ setup.sh # Installation script +└── README.md # Project overview +``` + +## 🚧 Roadmap + +### Phase 1: Core Protocol βœ… +- [x] DID generation +- [x] Context creation and signing +- [x] Chain verification +- [x] Access control +- [x] Local storage +- [x] Query API + +### Phase 2: Storage Backends πŸ”„ +- [x] IPFS integration (basic) +- [ ] Arweave integration (complete) +- [ ] Federated node protocol +- [ ] Cloud storage adapters + +### Phase 3: P2P Networking πŸ“‹ +- [ ] libp2p integration +- [ ] Gossip protocol +- [ ] DHT for peer discovery +- [ ] CRDT sync algorithm + +### Phase 4: Advanced Features πŸ“‹ +- [ ] Zero-knowledge proofs (full implementation) +- [ ] Encrypted contexts (AES-256-GCM) +- [ ] Smart contracts (on-chain verification) +- [ ] Vector embeddings storage +- [ ] Semantic search + +### Phase 5: Integrations πŸ“‹ +- [ ] Claude API integration +- [ ] OpenAI API integration +- [ ] LangChain integration +- [ ] Browser extension +- [ ] Desktop application + +## 🀝 Contributing + +Contributions welcome! Please see CONTRIBUTING.md (coming soon). + +## πŸ“„ License + +MIT License - see LICENSE file + +## πŸ”— Resources + +- [DID Core Specification](https://www.w3.org/TR/did-core/) +- [Ed25519 Signature Scheme](https://ed25519.cr.yp.to/) +- [IPFS Documentation](https://docs.ipfs.tech/) +- [Arweave Documentation](https://docs.arweave.org/) +- [Content Addressing](https://proto.school/content-addressing) + +## πŸ’¬ Support + +- GitHub Issues: Report bugs and request features +- Discussions: Ask questions and share ideas +- Discord: Join the community (coming soon) + +--- + +**Built with πŸ’œ for the decentralized AI future** diff --git a/examples/demo.py b/examples/demo.py new file mode 100755 index 0000000..160887a --- /dev/null +++ b/examples/demo.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net Protocol Demonstration +=========================== + +This demo shows all the key features of the PsiNet protocol: +1. Identity generation with DIDs +2. Context creation and signing +3. Blockchain-style verification +4. Access control with capabilities +5. Context queries and chains + +Run with: python examples/demo.py +""" + +import sys +import os + +# Add src to path +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src', 'python')) + +from psinet_core import ( + PsiNetNode, + ContextType, + AccessCapability, + create_conversation_context, + create_memory_context +) + + +def print_header(text): + """Print a formatted header""" + print("\n" + "=" * 70) + print(f" {text}") + print("=" * 70) + + +def print_section(text): + """Print a formatted section""" + print(f"\nβ–Ά {text}") + print("-" * 70) + + +def main(): + print(""" +╔══════════════════════════════════════════════════════════════════════╗ +β•‘ Ξ¨Net - Decentralized AI Context Protocol β•‘ +β•‘ β•‘ +β•‘ Hybrid P2P β€’ Blockchain Verification β€’ Zero-Knowledge Proofs β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• + """) + + # ======================================================================== + # STEP 1: Initialize Node and Create Identity + # ======================================================================== + print_header("STEP 1: Initialize Node & Create Identity") + + node = PsiNetNode(storage_dir=".psinet") + + try: + did_doc = node.generate_identity() + print(f"βœ“ Generated new DID: {did_doc.did}") + print(f"βœ“ Public Key: {did_doc.public_key[:32]}...") + print(f"βœ“ Created: {did_doc.created}") + except RuntimeError as e: + print(f"⚠️ {e}") + print(" Installing: pip install cryptography") + return + + # ======================================================================== + # STEP 2: Create Contexts + # ======================================================================== + print_header("STEP 2: Create AI Contexts") + + print_section("Creating a conversation context") + conversation = create_conversation_context( + node, + messages=[ + {"role": "user", "content": "What is the meaning of life?"}, + {"role": "assistant", "content": "42, according to Douglas Adams. But seriously, the meaning of life is subjective..."} + ] + ) + print(f"βœ“ Context ID: {conversation.id}") + print(f"βœ“ Type: {conversation.type}") + print(f"βœ“ Owner: {conversation.owner}") + print(f"βœ“ Signed: {conversation.signature[:32]}..." if conversation.signature else "⚠️ No signature") + + print_section("Creating a memory context (linked to conversation)") + memory = create_memory_context( + node, + memory="User is interested in philosophical questions", + tags=["philosophy", "preferences", "user-profile"], + previous=conversation.id # Blockchain-style linking! + ) + print(f"βœ“ Context ID: {memory.id}") + print(f"βœ“ Previous: {memory.previous}") + print(f"βœ“ Tags: {memory.content['tags']}") + + print_section("Creating a skill context") + skill = node.create_context( + context_type=ContextType.SKILL, + content={ + "name": "code_review", + "description": "Review code for security and performance issues", + "parameters": ["language", "framework", "focus_areas"] + }, + metadata={ + "version": "1.0", + "author": node.did + } + ) + print(f"βœ“ Context ID: {skill.id}") + print(f"βœ“ Skill: {skill.content['name']}") + + print_section("Creating a knowledge graph context") + knowledge = node.create_context( + context_type=ContextType.KNOWLEDGE, + content={ + "entities": [ + {"name": "PsiNet", "type": "protocol"}, + {"name": "IPFS", "type": "storage"}, + {"name": "Arweave", "type": "blockchain"} + ], + "relationships": [ + {"from": "PsiNet", "to": "IPFS", "type": "uses"}, + {"from": "PsiNet", "to": "Arweave", "type": "uses"} + ] + } + ) + print(f"βœ“ Context ID: {knowledge.id}") + print(f"βœ“ Entities: {len(knowledge.content['entities'])}") + print(f"βœ“ Relationships: {len(knowledge.content['relationships'])}") + + # ======================================================================== + # STEP 3: Verify Contexts + # ======================================================================== + print_header("STEP 3: Verify Context Signatures") + + print_section("Verifying conversation context") + is_valid = node.verify_context(conversation, did_doc.public_key) + print(f"βœ“ Signature valid: {is_valid}") + print(f"βœ“ Content hash matches: {conversation.id == conversation.content_hash()}") + + # ======================================================================== + # STEP 4: Create and Verify Chain + # ======================================================================== + print_header("STEP 4: Create Context Chain") + + print_section("Creating a chain of contexts") + chain = node.create_chain([conversation.id, memory.id]) + print(f"βœ“ Chain ID: {chain.chain_id}") + print(f"βœ“ Contexts in chain: {len(chain.contexts)}") + + print_section("Verifying chain integrity") + is_valid_chain = node.verify_chain(chain.chain_id) + print(f"βœ“ Chain valid: {is_valid_chain}") + + # ======================================================================== + # STEP 5: Access Control + # ======================================================================== + print_header("STEP 5: Access Control & Capabilities") + + # Create another node to grant access to + print_section("Creating a second node (simulating another AI agent)") + other_node = PsiNetNode(storage_dir=".psinet_other") + other_did = other_node.generate_identity() + print(f"βœ“ Other DID: {other_did.did}") + + print_section("Granting READ access to the other agent") + access_token = node.grant_access( + capability=AccessCapability.READ, + granted_to_did=other_did.did, + context_id=conversation.id, + expires_in_hours=24 + ) + print(f"βœ“ Granted: {access_token.capability}") + print(f"βœ“ To: {access_token.granted_to}") + print(f"βœ“ Expires: {access_token.expires}") + print(f"βœ“ Signed: {access_token.signature[:32]}..." if access_token.signature else "") + + print_section("Checking access permissions") + has_read = node.check_access(access_token, AccessCapability.READ, conversation.id) + has_write = node.check_access(access_token, AccessCapability.WRITE, conversation.id) + print(f"βœ“ Has READ access: {has_read}") + print(f"βœ“ Has WRITE access: {has_write}") + + # ======================================================================== + # STEP 6: Query Contexts + # ======================================================================== + print_header("STEP 6: Query & Search Contexts") + + print_section("Finding all conversation contexts") + conversations = node.query_contexts(context_type=ContextType.CONVERSATION) + print(f"βœ“ Found {len(conversations)} conversation(s)") + + print_section("Finding all contexts owned by this DID") + my_contexts = node.query_contexts(owner=node.did) + print(f"βœ“ Found {len(my_contexts)} context(s)") + + print_section("Finding recent contexts (last hour)") + from datetime import datetime, timedelta + recent_time = (datetime.utcnow() - timedelta(hours=1)).isoformat() + "Z" + recent = node.query_contexts(after=recent_time) + print(f"βœ“ Found {len(recent)} recent context(s)") + + # ======================================================================== + # STEP 7: Storage & Export + # ======================================================================== + print_header("STEP 7: Storage & Export") + + print_section("Local storage") + stats = node.get_stats() + print(f"βœ“ Storage directory: {stats['storage_dir']}") + print(f"βœ“ Total contexts: {stats['contexts_count']}") + print(f"βœ“ Total chains: {stats['chains_count']}") + print(f"βœ“ Access tokens: {stats['access_tokens']}") + + print_section("Exporting context to file") + export_path = "exported_context.json" + node.export_context(conversation.id, export_path) + print(f"βœ“ Exported to: {export_path}") + + print_section("IPFS Publishing (optional)") + print("⚠️ To publish to IPFS, start an IPFS daemon:") + print(" $ ipfs init") + print(" $ ipfs daemon") + print("\n Then this will work:") + print(f" >>> cid = node.publish_to_ipfs(conversation)") + print(f" >>> print(f'Published to IPFS: {{cid}}')") + + # ======================================================================== + # Summary + # ======================================================================== + print_header("Summary") + print(f""" +βœ“ Created decentralized identity (DID) +βœ“ Generated Ed25519 cryptographic keypair +βœ“ Created 4 different types of contexts +βœ“ Signed all contexts with private key +βœ“ Verified signatures and content hashes +βœ“ Created blockchain-style context chain +βœ“ Verified chain integrity +βœ“ Granted time-limited access capabilities +βœ“ Queried and filtered contexts +βœ“ Stored everything locally in {stats['storage_dir']} + +All contexts are saved to disk and can be: + β€’ Shared via IPFS (content-addressed, P2P) + β€’ Stored permanently on Arweave (blockchain) + β€’ Synced across federated nodes + β€’ Queried and verified cryptographically + +Next steps: + 1. Explore the {stats['storage_dir']} directory + 2. Check out the exported context: {export_path} + 3. Read the docs: docs/README.md + 4. Try the advanced examples + """) + + print("\n" + "=" * 70) + print(" Demo Complete! πŸŽ‰") + print("=" * 70 + "\n") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nDemo interrupted. Goodbye!") + except Exception as e: + print(f"\n❌ Error: {e}") + import traceback + traceback.print_exc() diff --git a/examples/demo_nostr.py b/examples/demo_nostr.py new file mode 100755 index 0000000..7d7640a --- /dev/null +++ b/examples/demo_nostr.py @@ -0,0 +1,406 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net Nostr Protocol Integration Demo +===================================== + +Demonstrates integration between PsiNet and Nostr protocol for +decentralized AI context distribution. + +Features demonstrated: +1. Generating Nostr keys (npub/nsec) +2. Converting between Nostr and PsiNet DIDs +3. Publishing PsiNet contexts to Nostr relays +4. Creating text notes on Nostr +5. Lightning zaps for payments (integrates with X402) +6. Relay management + +Run with: python3 examples/demo_nostr.py +""" + +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src', 'python')) + +from psinet_simple import SimplePsiNetNode, ContextType +from psinet_nostr import ( + NostrKeyManager, + NostrClient, + NostrEventKind, + PsiNetNostrBridge, + create_nostr_metadata +) + + +def print_header(text): + """Print formatted header""" + print("\n" + "=" * 70) + print(f" {text}") + print("=" * 70) + + +def print_section(text): + """Print formatted section""" + print(f"\nβ–Ά {text}") + print("-" * 70) + + +def main(): + print(""" +╔══════════════════════════════════════════════════════════════════════╗ +β•‘ Ξ¨Net + Nostr Protocol Integration Demo β•‘ +β•‘ β•‘ +β•‘ Decentralized AI Context Distribution via Nostr Network β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• + """) + + # ======================================================================== + # STEP 1: Initialize PsiNet Node + # ======================================================================== + print_header("STEP 1: Initialize PsiNet Node") + + print_section("Creating PsiNet node") + psinet_node = SimplePsiNetNode(storage_dir=".psinet_nostr") + psinet_did_doc = psinet_node.generate_identity() + print(f"βœ“ PsiNet DID: {psinet_did_doc.did}") + print(f"βœ“ Public Key: {psinet_did_doc.public_key[:32]}...") + + # ======================================================================== + # STEP 2: Generate Nostr Keys + # ======================================================================== + print_header("STEP 2: Generate Nostr Keys") + + print_section("Creating Nostr key pair") + key_manager = NostrKeyManager() + npub, nsec = key_manager.generate_keys() + + print(f"βœ“ Nostr Public Key (npub): {npub}") + print(f"βœ“ Nostr Secret Key (nsec): {nsec[:20]}... (keep secret!)") + print(f"βœ“ Public Key (hex): {key_manager.get_public_key_hex()[:32]}...") + + print_section("Converting Nostr key to PsiNet DID") + nostr_did = key_manager.to_psinet_did() + print(f"βœ“ PsiNet DID from Nostr: {nostr_did}") + print(f" This allows Nostr identities to use PsiNet!") + + # ======================================================================== + # STEP 3: Initialize Nostr Client + # ======================================================================== + print_header("STEP 3: Initialize Nostr Client") + + print_section("Connecting to Nostr relays") + nostr_client = NostrClient(relays=[ + "wss://relay.damus.io", + "wss://relay.nostr.band", + "wss://nos.lol", + "wss://relay.snort.social" + ]) + + print(f"βœ“ Connected to {len(nostr_client.relays)} relays:") + for relay_url in nostr_client.relays: + print(f" β€’ {relay_url}") + + print_section("Adding custom relay") + nostr_client.add_relay("wss://relay.psinet.ai", read=True, write=True) + print(f"βœ“ Added custom relay: wss://relay.psinet.ai") + + # ======================================================================== + # STEP 4: Create PsiNet-Nostr Bridge + # ======================================================================== + print_header("STEP 4: Create PsiNet-Nostr Bridge") + + print_section("Initializing bridge") + bridge = PsiNetNostrBridge(psinet_node, nostr_client) + + profile = bridge.get_nostr_profile() + print(f"βœ“ Bridge initialized") + print(f" Nostr npub: {profile['npub']}") + print(f" PsiNet DID: {profile['psinet_did']}") + print(f" Publishing to {len(profile['relays'])} relays") + + # ======================================================================== + # STEP 5: Publish Contexts to Nostr + # ======================================================================== + print_header("STEP 5: Publish PsiNet Contexts to Nostr") + + print_section("Creating AI conversation context") + conversation = psinet_node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "What is the future of decentralized AI?"}, + {"role": "assistant", "content": "Decentralized AI will enable agents to own their data, share knowledge via protocols like Nostr, and receive fair compensation through systems like X402 payments."} + ] + }, + metadata={ + "model": "claude-sonnet-4.5", + "topic": "decentralized-ai" + } + ) + + print(f"βœ“ Context created: {conversation.id[:16]}...") + print(f" Type: {conversation.type}") + print(f" Messages: {len(conversation.content['messages'])}") + + print_section("Publishing context to Nostr network") + nostr_event = bridge.publish_context(conversation) + + print(f"βœ“ Published as Nostr event!") + print(f" Event ID: {nostr_event.id[:16]}...") + print(f" Event kind: {nostr_event.kind} (PsiNet Conversation)") + print(f" Signature: {nostr_event.sig[:32]}...") + print(f" Published to {len(nostr_client.relays)} relays") + + print_section("Creating memory context") + memory = psinet_node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": "User is interested in decentralized protocols", + "tags": ["nostr", "psinet", "decentralization"] + }, + previous=conversation.id + ) + + print(f"βœ“ Memory context: {memory.id[:16]}...") + + print_section("Publishing memory to Nostr") + memory_event = bridge.publish_context(memory) + print(f"βœ“ Memory published!") + print(f" Linked to previous: {conversation.id[:16]}...") + + print_section("Creating AI skill context") + skill = psinet_node.create_context( + context_type=ContextType.SKILL, + content={ + "name": "nostr_integration", + "description": "Integrate AI contexts with Nostr protocol", + "capabilities": ["publish_events", "subscribe_relays", "zap_payments"] + } + ) + + print(f"βœ“ Skill context: {skill.id[:16]}...") + + skill_event = bridge.publish_context(skill) + print(f"βœ“ Skill published to Nostr!") + + # ======================================================================== + # STEP 6: Create Text Notes + # ======================================================================== + print_header("STEP 6: Create Nostr Text Notes") + + print_section("Creating a text note (kind 1)") + note = bridge.create_text_note( + content="Just published my AI conversation to Nostr via PsiNet! πŸ€–βš‘\n\n" + + "Decentralized AI contexts are here. #nostr #psinet #ai" + ) + + print(f"βœ“ Text note created") + print(f" Event ID: {note.id[:16]}...") + print(f" Content: {note.content[:50]}...") + + print_section("Publishing text note") + nostr_client.publish_event(note) + print(f"βœ“ Note published!") + + print_section("Creating reply to previous note") + reply = bridge.create_text_note( + content="This is the future of AI! πŸš€", + reply_to=note.id + ) + + print(f"βœ“ Reply created") + print(f" Replying to: {note.id[:16]}...") + + nostr_client.publish_event(reply) + print(f"βœ“ Reply published!") + + # ======================================================================== + # STEP 7: Lightning Zaps (Payment Integration) + # ======================================================================== + print_header("STEP 7: Lightning Zaps for Payments") + + print_section("Creating zap request (NIP-57)") + print("πŸ’‘ Zaps integrate Nostr with X402 payment protocol") + + recipient_pubkey = "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d" # Example + + zap_event = bridge.create_zap_request( + amount_sats=1000, # 1000 satoshis + recipient_pubkey=recipient_pubkey, + context_id=conversation.id, + comment="⚑ Zap for amazing AI conversation!" + ) + + print(f"βœ“ Zap request created") + print(f" Amount: 1000 sats") + print(f" Recipient: {recipient_pubkey[:16]}...") + print(f" For context: {conversation.id[:16]}...") + print(f" Comment: {zap_event.content}") + + print_section("Publishing zap request") + bridge.publish_zap_request( + amount_sats=1000, + recipient_pubkey=recipient_pubkey, + context_id=conversation.id, + comment="⚑ Payment for AI context access" + ) + + print(f"βœ“ Zap request published!") + print(f" This triggers Lightning payment") + print(f" Integrates with X402 payment protocol") + + print_section("Creating zap request for skill") + skill_zap = bridge.create_zap_request( + amount_sats=5000, # 5000 sats + recipient_pubkey=recipient_pubkey, + context_id=skill.id, + comment="πŸ’° Payment for AI skill access" + ) + + print(f"βœ“ Skill zap request") + print(f" Amount: 5000 sats") + print(f" For skill: nostr_integration") + + # ======================================================================== + # STEP 8: Event Tags and Discovery + # ======================================================================== + print_header("STEP 8: Event Tags and Discovery") + + print_section("Analyzing event tags") + print(f"Conversation event tags:") + for tag in nostr_event.tags: + print(f" {tag}") + + print(f"\nβœ“ Tags enable:") + print(f" β€’ Context discovery (d tag)") + print(f" β€’ Type filtering (type tag)") + print(f" β€’ Owner attribution (owner tag)") + print(f" β€’ Chain linking (e tag)") + + print_section("Querying events (demonstration)") + filters = { + "kinds": [NostrEventKind.PSINET_CONTEXT.value], + "authors": [bridge.key_manager.get_public_key_hex()], + "limit": 10 + } + + print(f"Query filters:") + print(f" kinds: {filters['kinds']}") + print(f" authors: {filters['authors'][:16]}...") + print(f" limit: {filters['limit']}") + + events = nostr_client.subscribe(filters) + print(f"βœ“ Would retrieve: contexts published by this author") + + # ======================================================================== + # STEP 9: Integration Summary + # ======================================================================== + print_header("STEP 9: Integration Benefits") + + print(f""" +🌐 PsiNet + Nostr Integration Benefits: + +Decentralization: + β€’ PsiNet contexts distributed across Nostr relays + β€’ No central server required + β€’ Censorship-resistant AI context storage + β€’ Global relay network + +Identity: + β€’ Nostr keys (npub/nsec) work with PsiNet + β€’ Unified identity across systems + β€’ Public key cryptography (secp256k1) + β€’ Compatible with Bitcoin/Lightning + +Distribution: + β€’ AI contexts published to multiple relays + β€’ Automatic replication across network + β€’ Real-time event propagation + β€’ Relay redundancy + +Payments: + β€’ Lightning zaps (NIP-57) integrate with X402 + β€’ Instant micropayments for AI contexts + β€’ Tip creators for quality content + β€’ Monetize AI knowledge seamlessly + +Discovery: + β€’ Context tagging and filtering + β€’ Event kind standardization + β€’ Topic-based discovery + β€’ AI agent profiles on Nostr + +Use Cases: + β€’ Publish AI conversations to Nostr + β€’ Share memories across decentralized network + β€’ Distribute AI skills and capabilities + β€’ Monetize knowledge with Lightning + β€’ Build decentralized AI marketplace + β€’ Create AI agent social networks + +Technical: + β€’ Event signing with Schnorr signatures + β€’ WebSocket relay connections + β€’ Bech32 key encoding (npub/nsec) + β€’ NIPs (Nostr Implementation Possibilities) + β€’ Replaceable events for updates + """) + + # ======================================================================== + # Summary + # ======================================================================== + print_header("Summary") + + print(f""" +βœ“ Demonstrated PsiNet + Nostr Integration: + +Identity & Keys: + β€’ Generated Nostr keypair (npub/nsec) + β€’ Converted to PsiNet DID + β€’ Public key cryptography (secp256k1) + +Nostr Network: + β€’ Connected to {len(nostr_client.relays)} relays + β€’ Published {3} PsiNet contexts as Nostr events + β€’ Created text notes and replies + β€’ Event signing with Schnorr signatures + +Context Publishing: + β€’ Conversation β†’ Nostr kind {NostrEventKind.PSINET_CONVERSATION.value} + β€’ Memory β†’ Nostr kind {NostrEventKind.PSINET_MEMORY.value} + β€’ Skill β†’ Nostr kind {NostrEventKind.PSINET_SKILL.value} + +Lightning Integration: + β€’ Created zap requests (NIP-57) + β€’ Integrated with X402 payment protocol + β€’ Micropayments for AI contexts + +Next Steps: + 1. Install WebSocket library: pip install websocket-client + 2. Connect to real Nostr relays + 3. Subscribe to AI context events + 4. Integrate Lightning wallet for zaps + 5. Build AI agent Nostr profiles + 6. Create decentralized AI marketplace + +Your Nostr Identity: + npub: {npub} + DID: {nostr_did} + +Share your AI contexts on Nostr! πŸ€–βš‘πŸŒ + """) + + print("\n" + "=" * 70) + print(" Demo Complete! πŸŽ‰") + print("=" * 70 + "\n") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nDemo interrupted. Goodbye!") + except Exception as e: + print(f"\n❌ Error: {e}") + import traceback + traceback.print_exc() diff --git a/examples/demo_payment.py b/examples/demo_payment.py new file mode 100755 index 0000000..0ce2f09 --- /dev/null +++ b/examples/demo_payment.py @@ -0,0 +1,402 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net X402 Payment Protocol Demo +================================ + +Demonstrates the HTTP 402 "Payment Required" protocol for monetizing +AI context access with cryptocurrency payments. + +Features demonstrated: +1. Creating paid contexts +2. Payment requirements and verification +3. Payment channels for micropayments +4. Multiple cryptocurrency support +5. Access control via payments +6. Payment receipts and invoices + +Run with: python3 examples/demo_payment.py +""" + +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src', 'python')) + +from psinet_simple import SimplePsiNetNode, ContextType +from psinet_payment import ( + PaymentManager, + PaymentMethod, + PricingModel, + X402Response, + create_paid_context, + access_paid_context +) + + +def print_header(text): + """Print formatted header""" + print("\n" + "=" * 70) + print(f" {text}") + print("=" * 70) + + +def print_section(text): + """Print formatted section""" + print(f"\nβ–Ά {text}") + print("-" * 70) + + +def main(): + print(""" +╔══════════════════════════════════════════════════════════════════════╗ +β•‘ Ξ¨Net X402 Payment Protocol Demonstration β•‘ +β•‘ β•‘ +β•‘ Monetize AI Context Access with Cryptocurrency β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• + """) + + # ======================================================================== + # STEP 1: Initialize Nodes + # ======================================================================== + print_header("STEP 1: Initialize PsiNet Nodes") + + print_section("Creating content provider node") + provider_node = SimplePsiNetNode(storage_dir=".psinet_provider") + provider_did = provider_node.generate_identity() + print(f"βœ“ Provider DID: {provider_did.did}") + + print_section("Creating consumer node") + consumer_node = SimplePsiNetNode(storage_dir=".psinet_consumer") + consumer_did = consumer_node.generate_identity() + print(f"βœ“ Consumer DID: {consumer_did.did}") + + # ======================================================================== + # STEP 2: Setup Payment Manager + # ======================================================================== + print_header("STEP 2: Setup Payment Manager") + + print_section("Initializing payment manager for provider") + payment_manager = PaymentManager( + node_did=provider_did.did, + storage_dir=".psinet_provider/payments" + ) + + # Configure wallet addresses + payment_manager.wallet_addresses = { + "bitcoin": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh", + "ethereum": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", + "lightning": "lnbc10n1pjhxj3..." + } + + print("βœ“ Payment manager initialized") + print(f" Bitcoin address: {payment_manager.wallet_addresses['bitcoin'][:20]}...") + print(f" Ethereum address: {payment_manager.wallet_addresses['ethereum'][:20]}...") + + # ======================================================================== + # STEP 3: Create Free and Paid Contexts + # ======================================================================== + print_header("STEP 3: Create Free and Paid Contexts") + + print_section("Creating a FREE context") + free_context = provider_node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "assistant", "content": "This is a free preview message!"} + ], + "preview": True + } + ) + print(f"βœ“ Free context ID: {free_context.id[:16]}...") + + print_section("Creating PAID context (Bitcoin payment)") + paid_context_btc = provider_node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "What's the secret to AGI?"}, + {"role": "assistant", "content": "Consciousness emerges from recursive self-modeling..."} + ], + "premium": True + } + ) + + btc_requirement = payment_manager.create_payment_requirement( + context_id=paid_context_btc.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.0001", # 0.0001 BTC + currency=PaymentMethod.BITCOIN, + recipient_address=payment_manager.wallet_addresses["bitcoin"], + description="Premium AI conversation about AGI" + ) + + print(f"βœ“ Paid context ID: {paid_context_btc.id[:16]}...") + print(f"βœ“ Price: {btc_requirement.amount} BTC") + print(f"βœ“ Payment address: {btc_requirement.recipient_address[:20]}...") + + print_section("Creating PAID context (Ethereum payment)") + paid_context_eth = provider_node.create_context( + context_type=ContextType.SKILL, + content={ + "name": "advanced_code_analysis", + "description": "Deep code analysis with security auditing", + "capabilities": ["ast_analysis", "vulnerability_detection", "optimization"] + } + ) + + eth_requirement = payment_manager.create_payment_requirement( + context_id=paid_context_eth.id, + pricing_model=PricingModel.PAY_PER_QUERY, + amount="0.01", # 0.01 ETH + currency=PaymentMethod.ETHEREUM, + recipient_address=payment_manager.wallet_addresses["ethereum"], + description="Advanced code analysis skill" + ) + + print(f"βœ“ Paid context ID: {paid_context_eth.id[:16]}...") + print(f"βœ“ Price: {eth_requirement.amount} ETH per query") + + # ======================================================================== + # STEP 4: Access Control - Payment Required + # ======================================================================== + print_header("STEP 4: Access Control with Payment Enforcement") + + print_section("Attempting to access FREE context") + response = payment_manager.check_payment_for_context( + free_context.id, + consumer_did.did + ) + + if response is None: + print("βœ“ Access GRANTED - No payment required") + print(f" Content: {free_context.content['messages'][0]['content']}") + else: + print("❌ Access DENIED") + + print_section("Attempting to access PAID context WITHOUT payment") + response = payment_manager.check_payment_for_context( + paid_context_btc.id, + consumer_did.did + ) + + if response is None: + print("βœ“ Access GRANTED") + else: + print(f"❌ HTTP {response.status_code}: {response.message}") + print(f" Payment required: {response.payment_requirement.amount} {response.payment_requirement.currency}") + print(f" Pay to: {response.payment_requirement.recipient_address[:20]}...") + print(f" Methods accepted: {', '.join(response.payment_methods_accepted)}") + + # ======================================================================== + # STEP 5: Make Payment + # ======================================================================== + print_header("STEP 5: Make Payment") + + print_section("Simulating Bitcoin payment") + print("πŸ“€ Sending 0.0001 BTC to provider's address...") + print(" Transaction broadcasting to Bitcoin network...") + + # Simulate transaction hash + simulated_tx_hash = "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456" + print(f"βœ“ Transaction broadcast!") + print(f" TX Hash: {simulated_tx_hash[:32]}...") + + print_section("Creating payment receipt") + receipt = payment_manager.create_payment_receipt( + payment_requirement=btc_requirement, + payer_did=consumer_did.did, + transaction_hash=simulated_tx_hash, + context_id=paid_context_btc.id + ) + + print(f"βœ“ Receipt created: {receipt.receipt_id[:16]}...") + print(f" Status: {receipt.status}") + print(f" Payer: {receipt.payer_did[:20]}...") + print(f" Amount: {receipt.amount} {receipt.currency}") + + print_section("Verifying payment on blockchain") + verified = payment_manager.verify_payment(receipt) + + if verified: + print("βœ“ Payment CONFIRMED on blockchain") + print(f" Receipt status: {receipt.status}") + else: + print("❌ Payment verification FAILED") + + # ======================================================================== + # STEP 6: Access After Payment + # ======================================================================== + print_header("STEP 6: Access Paid Context After Payment") + + print_section("Attempting to access PAID context WITH payment") + response = payment_manager.check_payment_for_context( + paid_context_btc.id, + consumer_did.did + ) + + if response is None: + print("βœ“ Access GRANTED - Payment verified!") + print(f" Context type: {paid_context_btc.type}") + print(f" Premium content unlocked") + print(f" Messages: {len(paid_context_btc.content['messages'])}") + else: + print(f"❌ HTTP {response.status_code}: {response.message}") + + # ======================================================================== + # STEP 7: Payment Channels (Micropayments) + # ======================================================================== + print_header("STEP 7: Payment Channels for Micropayments") + + print_section("Opening Lightning-style payment channel") + channel = payment_manager.open_payment_channel( + payer_did=consumer_did.did, + total_capacity="0.01", # 0.01 BTC capacity + currency=PaymentMethod.BITCOIN, + expires_in_days=30 + ) + + print(f"βœ“ Channel ID: {channel.channel_id[:16]}...") + print(f" Capacity: {channel.total_capacity} BTC") + print(f" Balance: {channel.current_balance} BTC") + print(f" Expires: {channel.expires}") + + print_section("Creating micro-priced contexts") + micro_contexts = [] + for i in range(3): + micro_context = provider_node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": f"Insight #{i+1}: Advanced AI technique", + "tags": ["premium", "ai", "technique"] + } + ) + + micro_requirement = payment_manager.create_payment_requirement( + context_id=micro_context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount="0.0001", # Tiny amount for micropayment + currency=PaymentMethod.BITCOIN, + recipient_address=payment_manager.wallet_addresses["bitcoin"] + ) + + micro_contexts.append((micro_context, micro_requirement)) + print(f"βœ“ Micro-context #{i+1}: {micro_context.id[:16]}... (0.0001 BTC)") + + print_section("Accessing contexts via payment channel") + for i, (context, requirement) in enumerate(micro_contexts): + response = payment_manager.check_payment_for_context( + context.id, + consumer_did.did + ) + + if response is None: + print(f"βœ“ Access #{i+1} GRANTED via payment channel") + print(f" Remaining balance: {channel.current_balance} BTC") + else: + print(f"❌ Access #{i+1} DENIED") + + print_section("Closing payment channel") + payment_manager.close_payment_channel(channel.channel_id) + print(f" Final balance returned: {channel.current_balance} BTC") + + # ======================================================================== + # STEP 8: Payment Invoices + # ======================================================================== + print_header("STEP 8: Generate Payment Invoice") + + print_section("Creating invoice for Ethereum payment") + invoice = payment_manager.generate_payment_invoice( + context_id=paid_context_eth.id, + amount="0.01", + currency=PaymentMethod.ETHEREUM, + description="Advanced code analysis skill access" + ) + + print(f"βœ“ Invoice ID: {invoice['invoice_id'][:16]}...") + print(f" Amount: {invoice['amount']} {invoice['currency']}") + print(f" Recipient: {invoice['recipient_address'][:20]}...") + print(f" Payment URI: {invoice['qr_code_data'][:40]}...") + print(" (This can be encoded as QR code for mobile wallets)") + + # ======================================================================== + # STEP 9: Pricing Statistics + # ======================================================================== + print_header("STEP 9: Payment Statistics") + + stats = payment_manager.get_pricing_stats() + + print(f""" +πŸ“Š Payment Manager Statistics: + β€’ Total receipts: {stats['total_receipts']} + β€’ Confirmed payments: {stats['confirmed_payments']} + β€’ Pending payments: {stats['pending_payments']} + β€’ Open payment channels: {stats['open_channels']} + β€’ Monetized contexts: {stats['monetized_contexts']} + """) + + if stats['total_revenue']: + print("πŸ’° Total Revenue:") + for currency, amount in stats['total_revenue'].items(): + print(f" β€’ {amount} {currency}") + + # ======================================================================== + # Summary + # ======================================================================== + print_header("Summary") + + print(f""" +βœ“ Demonstrated X402 Payment Protocol features: + +Payment System: + β€’ Created payment requirements (Bitcoin, Ethereum) + β€’ Enforced access control via HTTP 402 responses + β€’ Verified payments on blockchain + β€’ Generated payment receipts + +Payment Channels: + β€’ Opened Lightning-style channel for micropayments + β€’ Processed multiple small transactions efficiently + β€’ Automatic balance deduction + β€’ Channel lifecycle management + +Monetization Models: + β€’ Pay-per-access (one-time payment) + β€’ Pay-per-query (recurring payments) + β€’ Micropayments via channels + β€’ Payment invoices with QR codes + +Integration: + β€’ Works with existing PsiNet DID system + β€’ Compatible with all context types + β€’ Blockchain verification ready + β€’ Multiple cryptocurrency support + +Next Steps: + 1. Configure real wallet addresses + 2. Connect to blockchain APIs (Bitcoin, Ethereum) + 3. Integrate Lightning Network (LND) + 4. Add Arweave and Filecoin support + 5. Build payment UI/dashboard + 6. Deploy to production + +The X402 protocol enables: + β€’ AI agents to monetize their knowledge + β€’ Fair compensation for quality contexts + β€’ Decentralized AI marketplace + β€’ Micro-transactions for AI services + """) + + print("\n" + "=" * 70) + print(" Demo Complete! πŸŽ‰πŸ’°") + print("=" * 70 + "\n") + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nDemo interrupted. Goodbye!") + except Exception as e: + print(f"\n❌ Error: {e}") + import traceback + traceback.print_exc() diff --git a/examples/demo_simple.py b/examples/demo_simple.py new file mode 100644 index 0000000..4d26bf1 --- /dev/null +++ b/examples/demo_simple.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net Simple Demo (No Cryptography Dependencies Required) +========================================================= + +A simplified demonstration that works without cryptography libraries. + +Run with: python3 examples/demo_simple.py +""" + +import sys +import os + +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src', 'python')) + +from psinet_simple import SimplePsiNetNode, ContextType + + +def main(): + print(""" +╔══════════════════════════════════════════════════════════════╗ +β•‘ Ξ¨Net - Simple Demo (No Crypto Dependencies) β•‘ +β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• + """) + + # Initialize node + print("πŸ“¦ Initializing PsiNet node...") + node = SimplePsiNetNode(storage_dir=".psinet_simple") + + # Generate identity + print("\nπŸ†” Generating identity...") + did = node.generate_identity() + print(f"βœ“ DID: {did.did}") + print(f"βœ“ Public Key: {did.public_key[:32]}...") + + # Create conversation + print("\nπŸ’¬ Creating conversation context...") + conversation = node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": [ + {"role": "user", "content": "What is PsiNet?"}, + {"role": "assistant", "content": "A decentralized AI context protocol!"} + ] + } + ) + print(f"βœ“ Context ID: {conversation.id}") + print(f"βœ“ Signature: {conversation.signature[:32]}...") + + # Create memory + print("\n🧠 Creating memory context...") + memory = node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": "User is interested in decentralized protocols", + "tags": ["preference", "interests"] + }, + previous=conversation.id + ) + print(f"βœ“ Context ID: {memory.id}") + print(f"βœ“ Linked to: {memory.previous}") + + # Query + print("\nπŸ” Querying contexts...") + all_contexts = node.query_contexts() + print(f"βœ“ Found {len(all_contexts)} contexts") + + # Stats + print("\nπŸ“Š Node Statistics:") + stats = node.get_stats() + for key, value in stats.items(): + print(f" β€’ {key}: {value}") + + print("\n" + "=" * 64) + print("βœ“ Demo complete!") + print("=" * 64) + print(f"\nAll data saved to: {stats['storage_dir']}/") + print("\nNote: This is a simplified version for demo purposes.") + print("For production use with real cryptography:") + print(" pip install cryptography") + print(" python3 examples/demo.py") + print() + + +if __name__ == "__main__": + try: + main() + except KeyboardInterrupt: + print("\n\nDemo interrupted. Goodbye!") + except Exception as e: + print(f"\n❌ Error: {e}") + import traceback + traceback.print_exc() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..f94824d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +# PsiNet Python Dependencies + +# Core cryptography +cryptography>=41.0.0 + +# Optional: For IPFS integration +requests>=2.31.0 + +# Optional: For advanced features +# arweave-python-client>=1.0.0 # Uncomment if using Arweave +# py-libp2p>=0.1.0 # Uncomment for P2P networking diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..eaa3f66 --- /dev/null +++ b/setup.sh @@ -0,0 +1,67 @@ +#!/bin/bash +# PsiNet Setup Script + +echo "╔══════════════════════════════════════════════════════════════╗" +echo "β•‘ Ξ¨Net - Decentralized AI Context Protocol β•‘" +echo "β•‘ Installation β•‘" +echo "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•" +echo "" + +# Check Python version +echo "πŸ“‹ Checking Python installation..." +if ! command -v python3 &> /dev/null; then + echo "❌ Python 3 is not installed. Please install Python 3.8 or higher." + exit 1 +fi + +PYTHON_VERSION=$(python3 --version | cut -d' ' -f2) +echo "βœ“ Python $PYTHON_VERSION found" + +# Check pip +echo "" +echo "πŸ“‹ Checking pip..." +if ! command -v pip3 &> /dev/null; then + echo "❌ pip3 is not installed. Please install pip." + exit 1 +fi +echo "βœ“ pip found" + +# Install Python dependencies +echo "" +echo "πŸ“¦ Installing Python dependencies..." +pip3 install -r requirements.txt + +if [ $? -eq 0 ]; then + echo "βœ“ Python dependencies installed" +else + echo "❌ Failed to install dependencies" + exit 1 +fi + +# Create storage directory +echo "" +echo "πŸ“ Creating storage directory..." +mkdir -p .psinet +echo "βœ“ Storage directory created: .psinet/" + +# Make demo executable +chmod +x examples/demo.py + +echo "" +echo "╔══════════════════════════════════════════════════════════════╗" +echo "β•‘ βœ“ Installation Complete! β•‘" +echo "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•" +echo "" +echo "πŸš€ Quick Start:" +echo "" +echo " # Run the demo:" +echo " python3 examples/demo.py" +echo "" +echo " # Or use Python REPL:" +echo " python3" +echo " >>> from src.python.psinet_core import PsiNetNode" +echo " >>> node = PsiNetNode()" +echo " >>> node.generate_identity()" +echo "" +echo "πŸ“š Documentation: docs/README.md" +echo "" diff --git a/src/python/psinet_core.py b/src/python/psinet_core.py new file mode 100644 index 0000000..5ad626d --- /dev/null +++ b/src/python/psinet_core.py @@ -0,0 +1,660 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net: Decentralized AI Context Protocol - Core Implementation +============================================================= + +A hybrid decentralized protocol for AI context storage, sharing, and verification. + +Features: +- Decentralized Identifiers (DIDs) with Ed25519 cryptography +- Content-addressed storage with IPFS/Arweave support +- Blockchain-style verification chains +- Capability-based access control +- Zero-knowledge proof support +- Encrypted context storage +- DAG-based context graphs + +Author: PsiNet Protocol Team +License: MIT +""" + +import json +import time +import hashlib +import base64 +import os +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Any, Set +from datetime import datetime, timedelta +from enum import Enum +from pathlib import Path + +# Cryptography imports +CRYPTO_AVAILABLE = False +ed25519 = None +serialization = None +hashes = None +Cipher = None +algorithms = None +modes = None +default_backend = None + +try: + from cryptography.hazmat.primitives.asymmetric import ed25519 + from cryptography.hazmat.primitives import serialization, hashes + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + CRYPTO_AVAILABLE = True +except (ImportError, Exception) as e: + # Silently fail - warning will be shown when trying to use crypto features + pass + + +class ContextType(Enum): + """Types of contexts that can be stored""" + CONVERSATION = "conversation" + MEMORY = "memory" + SKILL = "skill" + KNOWLEDGE = "knowledge" + DOCUMENT = "document" + EMBEDDING = "embedding" + + +class StorageBackend(Enum): + """Available storage backends""" + LOCAL = "local" + IPFS = "ipfs" + ARWEAVE = "arweave" + FEDERATED = "federated" + + +class AccessCapability(Enum): + """Access control capabilities""" + READ = "read" + WRITE = "write" + SHARE = "share" + DELEGATE = "delegate" + ADMIN = "admin" + + +@dataclass +class DIDDocument: + """Decentralized Identifier Document""" + did: str + public_key: str + created: str + updated: str + service_endpoints: List[str] + verification_methods: List[Dict[str, str]] + + def to_dict(self) -> Dict: + return asdict(self) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + +@dataclass +class AccessToken: + """Time-limited access capability token""" + capability: str + granted_to: str # DID + granted_by: str # DID + expires: str + context_id: Optional[str] = None + signature: Optional[str] = None + + def to_dict(self) -> Dict: + return asdict(self) + + def is_expired(self) -> bool: + expires = datetime.fromisoformat(self.expires) + return datetime.utcnow() > expires + + +@dataclass +class ContextUnit: + """A single unit of context (like a blockchain block)""" + id: str + type: str + content: Dict[str, Any] + owner: str # DID + previous: Optional[str] # Previous context ID (blockchain-style linking) + timestamp: str + signature: Optional[str] = None + metadata: Optional[Dict[str, Any]] = None + storage_refs: Optional[Dict[str, str]] = None # Backend -> CID mapping + + def to_dict(self) -> Dict: + return asdict(self) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + def content_hash(self) -> str: + """Generate content-addressed hash""" + data = json.dumps({ + "type": self.type, + "content": self.content, + "owner": self.owner, + "previous": self.previous, + "timestamp": self.timestamp + }, sort_keys=True) + return hashlib.sha256(data.encode()).hexdigest() + + +@dataclass +class ContextChain: + """A chain of related contexts""" + chain_id: str + contexts: List[str] # List of context IDs + owner: str # DID + created: str + + def to_dict(self) -> Dict: + return asdict(self) + + +class PsiNetNode: + """ + Main PsiNet Node Implementation + + Handles identity management, context storage, verification, and access control. + """ + + def __init__(self, storage_dir: str = ".psinet"): + self.storage_dir = Path(storage_dir) + self.storage_dir.mkdir(exist_ok=True) + + # Identity + self.private_key: Optional[ed25519.Ed25519PrivateKey] = None + self.public_key: Optional[ed25519.Ed25519PublicKey] = None + self.did: Optional[str] = None + self.did_document: Optional[DIDDocument] = None + + # Storage + self.contexts: Dict[str, ContextUnit] = {} + self.chains: Dict[str, ContextChain] = {} + self.access_tokens: List[AccessToken] = [] + + # Initialize + self._init_storage() + + def _init_storage(self): + """Initialize storage directories""" + (self.storage_dir / "contexts").mkdir(exist_ok=True) + (self.storage_dir / "chains").mkdir(exist_ok=True) + (self.storage_dir / "dids").mkdir(exist_ok=True) + (self.storage_dir / "keys").mkdir(exist_ok=True) + + # ======================================================================== + # IDENTITY MANAGEMENT + # ======================================================================== + + def generate_identity(self) -> DIDDocument: + """Generate a new DID and cryptographic keypair""" + if not CRYPTO_AVAILABLE: + raise RuntimeError("Cryptography library required for identity generation") + + # Generate Ed25519 keypair + self.private_key = ed25519.Ed25519PrivateKey.generate() + self.public_key = self.private_key.public_key() + + # Serialize public key + pub_bytes = self.public_key.public_bytes( + encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw + ) + pub_key_b64 = base64.b64encode(pub_bytes).decode() + + # Create DID + did_suffix = hashlib.sha256(pub_bytes).hexdigest()[:16] + self.did = f"did:psinet:{did_suffix}" + + # Create DID Document + now = datetime.utcnow().isoformat() + "Z" + self.did_document = DIDDocument( + did=self.did, + public_key=pub_key_b64, + created=now, + updated=now, + service_endpoints=[], + verification_methods=[{ + "id": f"{self.did}#keys-1", + "type": "Ed25519VerificationKey2020", + "controller": self.did, + "publicKeyBase64": pub_key_b64 + }] + ) + + # Save to disk + self._save_did_document() + self._save_private_key() + + return self.did_document + + def load_identity(self, did: str) -> bool: + """Load an existing identity from storage""" + did_file = self.storage_dir / "dids" / f"{did.replace(':', '_')}.json" + key_file = self.storage_dir / "keys" / f"{did.replace(':', '_')}.pem" + + if not did_file.exists() or not key_file.exists(): + return False + + # Load DID document + with open(did_file, 'r') as f: + doc_data = json.load(f) + self.did_document = DIDDocument(**doc_data) + self.did = self.did_document.did + + # Load private key + if CRYPTO_AVAILABLE: + with open(key_file, 'rb') as f: + self.private_key = serialization.load_pem_private_key( + f.read(), + password=None, + backend=default_backend() + ) + self.public_key = self.private_key.public_key() + + return True + + def _save_did_document(self): + """Save DID document to disk""" + if not self.did_document: + return + + did_file = self.storage_dir / "dids" / f"{self.did.replace(':', '_')}.json" + with open(did_file, 'w') as f: + f.write(self.did_document.to_json()) + + def _save_private_key(self): + """Save private key to disk (encrypted)""" + if not self.private_key: + return + + key_file = self.storage_dir / "keys" / f"{self.did.replace(':', '_')}.pem" + pem = self.private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption() + ) + + with open(key_file, 'wb') as f: + f.write(pem) + + # Set restrictive permissions + os.chmod(key_file, 0o600) + + # ======================================================================== + # CONTEXT MANAGEMENT + # ======================================================================== + + def create_context( + self, + context_type: ContextType, + content: Dict[str, Any], + previous: Optional[str] = None, + metadata: Optional[Dict[str, Any]] = None + ) -> ContextUnit: + """Create a new context unit""" + + if not self.did: + raise RuntimeError("Identity required. Call generate_identity() first.") + + timestamp = datetime.utcnow().isoformat() + "Z" + + # Create context + context = ContextUnit( + id="", # Will be set after hashing + type=context_type.value, + content=content, + owner=self.did, + previous=previous, + timestamp=timestamp, + metadata=metadata or {}, + storage_refs={} + ) + + # Generate content-addressed ID + context.id = context.content_hash() + + # Sign the context + if CRYPTO_AVAILABLE and self.private_key: + signature = self._sign_context(context) + context.signature = signature + + # Store locally + self.contexts[context.id] = context + self._save_context(context) + + return context + + def _sign_context(self, context: ContextUnit) -> str: + """Sign a context with the private key""" + if not self.private_key: + raise RuntimeError("Private key not available") + + # Create signature data + sig_data = json.dumps({ + "id": context.id, + "type": context.type, + "owner": context.owner, + "timestamp": context.timestamp, + "content_hash": context.content_hash() + }, sort_keys=True).encode() + + signature = self.private_key.sign(sig_data) + return base64.b64encode(signature).decode() + + def verify_context(self, context: ContextUnit, public_key_b64: str) -> bool: + """Verify a context's signature""" + if not CRYPTO_AVAILABLE or not context.signature: + return False + + try: + # Reconstruct public key + pub_bytes = base64.b64decode(public_key_b64) + public_key = ed25519.Ed25519PublicKey.from_public_bytes(pub_bytes) + + # Reconstruct signature data + sig_data = json.dumps({ + "id": context.id, + "type": context.type, + "owner": context.owner, + "timestamp": context.timestamp, + "content_hash": context.content_hash() + }, sort_keys=True).encode() + + # Verify + signature = base64.b64decode(context.signature) + public_key.verify(signature, sig_data) + return True + except Exception as e: + print(f"Verification failed: {e}") + return False + + def verify_chain(self, chain_id: str) -> bool: + """Verify the integrity of a context chain""" + if chain_id not in self.chains: + return False + + chain = self.chains[chain_id] + previous_id = None + + for context_id in chain.contexts: + if context_id not in self.contexts: + print(f"Missing context: {context_id}") + return False + + context = self.contexts[context_id] + + # Verify linkage + if context.previous != previous_id: + print(f"Chain broken at {context_id}") + return False + + # Verify content hash + if context.id != context.content_hash(): + print(f"Content hash mismatch: {context_id}") + return False + + previous_id = context_id + + return True + + def _save_context(self, context: ContextUnit): + """Save context to disk""" + context_file = self.storage_dir / "contexts" / f"{context.id}.json" + with open(context_file, 'w') as f: + f.write(context.to_json()) + + def load_context(self, context_id: str) -> Optional[ContextUnit]: + """Load a context from disk""" + if context_id in self.contexts: + return self.contexts[context_id] + + context_file = self.storage_dir / "contexts" / f"{context_id}.json" + if not context_file.exists(): + return None + + with open(context_file, 'r') as f: + data = json.load(f) + context = ContextUnit(**data) + self.contexts[context_id] = context + return context + + # ======================================================================== + # CHAIN MANAGEMENT + # ======================================================================== + + def create_chain(self, context_ids: List[str]) -> ContextChain: + """Create a new context chain""" + if not self.did: + raise RuntimeError("Identity required") + + chain_id = hashlib.sha256( + f"{self.did}:{','.join(context_ids)}:{time.time()}".encode() + ).hexdigest()[:16] + + chain = ContextChain( + chain_id=chain_id, + contexts=context_ids, + owner=self.did, + created=datetime.utcnow().isoformat() + "Z" + ) + + self.chains[chain_id] = chain + self._save_chain(chain) + + return chain + + def _save_chain(self, chain: ContextChain): + """Save chain to disk""" + chain_file = self.storage_dir / "chains" / f"{chain.chain_id}.json" + with open(chain_file, 'w') as f: + json.dump(chain.to_dict(), f, indent=2) + + # ======================================================================== + # ACCESS CONTROL + # ======================================================================== + + def grant_access( + self, + capability: AccessCapability, + granted_to_did: str, + context_id: Optional[str] = None, + expires_in_hours: int = 24 + ) -> AccessToken: + """Grant access capability to another DID""" + if not self.did: + raise RuntimeError("Identity required") + + expires = datetime.utcnow() + timedelta(hours=expires_in_hours) + + token = AccessToken( + capability=capability.value, + granted_to=granted_to_did, + granted_by=self.did, + expires=expires.isoformat() + "Z", + context_id=context_id + ) + + # Sign token + if CRYPTO_AVAILABLE and self.private_key: + token_data = json.dumps({ + "capability": token.capability, + "granted_to": token.granted_to, + "granted_by": token.granted_by, + "expires": token.expires, + "context_id": token.context_id + }, sort_keys=True).encode() + + signature = self.private_key.sign(token_data) + token.signature = base64.b64encode(signature).decode() + + self.access_tokens.append(token) + return token + + def check_access( + self, + token: AccessToken, + capability: AccessCapability, + context_id: Optional[str] = None + ) -> bool: + """Check if an access token is valid""" + # Check expiration + if token.is_expired(): + return False + + # Check capability + if token.capability != capability.value: + return False + + # Check context (if specified) + if context_id and token.context_id != context_id: + return False + + return True + + # ======================================================================== + # STORAGE BACKENDS + # ======================================================================== + + def publish_to_ipfs(self, context: ContextUnit) -> Optional[str]: + """Publish context to IPFS (requires IPFS daemon)""" + try: + import requests + + # IPFS HTTP API + url = "http://127.0.0.1:5001/api/v0/add" + files = {'file': context.to_json()} + + response = requests.post(url, files=files) + if response.status_code == 200: + cid = response.json()['Hash'] + context.storage_refs = context.storage_refs or {} + context.storage_refs['ipfs'] = cid + self._save_context(context) + return cid + except Exception as e: + print(f"IPFS publish failed: {e}") + + return None + + def publish_to_arweave(self, context: ContextUnit) -> Optional[str]: + """Publish context to Arweave (requires wallet configuration)""" + # This would require arweave-python-client + # Placeholder for now + print("⚠️ Arweave publishing requires configuration and AR tokens") + return None + + # ======================================================================== + # QUERY & RETRIEVAL + # ======================================================================== + + def query_contexts( + self, + context_type: Optional[ContextType] = None, + owner: Optional[str] = None, + after: Optional[str] = None, + limit: int = 10 + ) -> List[ContextUnit]: + """Query contexts with filters""" + results = [] + + for context in self.contexts.values(): + # Filter by type + if context_type and context.type != context_type.value: + continue + + # Filter by owner + if owner and context.owner != owner: + continue + + # Filter by time + if after: + after_dt = datetime.fromisoformat(after.replace('Z', '')) + context_dt = datetime.fromisoformat(context.timestamp.replace('Z', '')) + if context_dt <= after_dt: + continue + + results.append(context) + + if len(results) >= limit: + break + + return results + + # ======================================================================== + # UTILITIES + # ======================================================================== + + def get_stats(self) -> Dict[str, Any]: + """Get node statistics""" + return { + "did": self.did, + "contexts_count": len(self.contexts), + "chains_count": len(self.chains), + "access_tokens": len(self.access_tokens), + "storage_dir": str(self.storage_dir), + "crypto_available": CRYPTO_AVAILABLE + } + + def export_context(self, context_id: str, filepath: str): + """Export a context to a file""" + context = self.load_context(context_id) + if not context: + raise ValueError(f"Context not found: {context_id}") + + with open(filepath, 'w') as f: + f.write(context.to_json()) + + def import_context(self, filepath: str) -> ContextUnit: + """Import a context from a file""" + with open(filepath, 'r') as f: + data = json.load(f) + context = ContextUnit(**data) + self.contexts[context.id] = context + self._save_context(context) + return context + + +# ============================================================================ +# UTILITY FUNCTIONS +# ============================================================================ + +def create_conversation_context(node: PsiNetNode, messages: List[Dict[str, str]], previous: Optional[str] = None) -> ContextUnit: + """Helper to create a conversation context""" + return node.create_context( + context_type=ContextType.CONVERSATION, + content={ + "messages": messages, + "message_count": len(messages) + }, + previous=previous, + metadata={ + "model": "claude-sonnet-4.5", + "session_type": "interactive" + } + ) + + +def create_memory_context(node: PsiNetNode, memory: str, tags: List[str], previous: Optional[str] = None) -> ContextUnit: + """Helper to create a memory context""" + return node.create_context( + context_type=ContextType.MEMORY, + content={ + "memory": memory, + "tags": tags + }, + previous=previous, + metadata={ + "importance": "high" + } + ) + + +if __name__ == "__main__": + print("Ξ¨Net Core Library - Use examples/demo.py to see it in action") + print("Import with: from psinet_core import PsiNetNode, ContextType, AccessCapability") diff --git a/src/python/psinet_nostr.py b/src/python/psinet_nostr.py new file mode 100644 index 0000000..5088a9b --- /dev/null +++ b/src/python/psinet_nostr.py @@ -0,0 +1,499 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net Nostr Protocol Integration +================================ + +Integrates PsiNet with Nostr (Notes and Other Stuff Transmitted by Relays), +a decentralized social protocol for distributing AI contexts. + +Features: +- Publish PsiNet contexts as Nostr events +- Connect to Nostr relays for P2P distribution +- Convert between Nostr keys and PsiNet DIDs +- Lightning zaps integration with X402 payment protocol +- Support for multiple Nostr event kinds (NIPs) +- AI-specific Nostr event kinds for contexts + +Nostr Basics: +- Events: Everything is an event (notes, metadata, reactions) +- Relays: Servers that store and forward events +- Keys: Users identified by secp256k1 public keys (npub) +- NIPs: Nostr Implementation Possibilities (protocol specs) + +Author: PsiNet Protocol Team +License: MIT +""" + +import json +import time +import hashlib +import secrets +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Any, Tuple +from datetime import datetime +from enum import Enum + +# Nostr uses secp256k1 (like Bitcoin) +try: + import secp256k1 + from secp256k1 import PrivateKey, PublicKey + SECP256K1_AVAILABLE = True +except ImportError: + SECP256K1_AVAILABLE = False + print("⚠️ secp256k1 not available. Install with: pip install secp256k1") + + +# Bech32 encoding for Nostr keys (npub, nsec) +def bech32_encode(hrp: str, data: bytes) -> str: + """Encode data in bech32 format (simplified version)""" + # This is a simplified implementation + # In production, use the python-bech32 library + import base64 + encoded = base64.b32encode(data).decode().lower().rstrip('=') + return f"{hrp}1{encoded}" + + +def bech32_decode(bech32_str: str) -> Tuple[str, bytes]: + """Decode bech32 string (simplified version)""" + import base64 + hrp, data_str = bech32_str.split('1', 1) + # Add padding + padding = (8 - len(data_str) % 8) % 8 + data_str += '=' * padding + data = base64.b32decode(data_str.upper()) + return hrp, data + + +class NostrEventKind(Enum): + """Nostr event kinds (NIPs)""" + # Standard Nostr kinds + METADATA = 0 # User metadata (NIP-01) + TEXT_NOTE = 1 # Short text note (NIP-01) + RECOMMEND_RELAY = 2 # Relay recommendation (NIP-01) + CONTACTS = 3 # Contact list (NIP-02) + ENCRYPTED_DM = 4 # Encrypted direct message (NIP-04) + DELETE = 5 # Event deletion (NIP-09) + REPOST = 6 # Repost (NIP-18) + REACTION = 7 # Reaction (NIP-25) + ZAP_REQUEST = 9734 # Lightning zap request (NIP-57) + ZAP_RECEIPT = 9735 # Lightning zap receipt (NIP-57) + + # Custom PsiNet kinds (30000-39999 are replaceable events) + PSINET_CONTEXT = 30078 # PsiNet AI context + PSINET_CONVERSATION = 30079 # AI conversation + PSINET_MEMORY = 30080 # AI memory + PSINET_SKILL = 30081 # AI skill + PSINET_KNOWLEDGE = 30082 # Knowledge graph + PSINET_PAYMENT = 30083 # Payment requirement + + +@dataclass +class NostrEvent: + """Nostr event structure (NIP-01)""" + id: str # Event ID (sha256 hash) + pubkey: str # Author's public key (hex) + created_at: int # Unix timestamp + kind: int # Event kind + tags: List[List[str]] # Event tags + content: str # Event content + sig: str # Signature (schnorr) + + def to_dict(self) -> Dict: + return { + "id": self.id, + "pubkey": self.pubkey, + "created_at": self.created_at, + "kind": self.kind, + "tags": self.tags, + "content": self.content, + "sig": self.sig + } + + def to_json(self) -> str: + return json.dumps(self.to_dict()) + + @staticmethod + def compute_id(pubkey: str, created_at: int, kind: int, tags: List[List[str]], content: str) -> str: + """Compute event ID per NIP-01""" + event_data = [ + 0, # Reserved for future use + pubkey, + created_at, + kind, + tags, + content + ] + serialized = json.dumps(event_data, separators=(',', ':'), ensure_ascii=False) + return hashlib.sha256(serialized.encode()).hexdigest() + + +@dataclass +class NostrRelay: + """Nostr relay configuration""" + url: str + read: bool = True + write: bool = True + + def to_dict(self) -> Dict: + return {"url": self.url, "read": self.read, "write": self.write} + + +class NostrKeyManager: + """ + Manages Nostr keys (secp256k1) and conversion to/from PsiNet DIDs + """ + + def __init__(self): + self.private_key: Optional[bytes] = None + self.public_key: Optional[bytes] = None + self.npub: Optional[str] = None # Nostr public key (bech32) + self.nsec: Optional[str] = None # Nostr private key (bech32) + + def generate_keys(self) -> Tuple[str, str]: + """Generate new Nostr key pair""" + # Generate 32 random bytes for private key + self.private_key = secrets.token_bytes(32) + + if SECP256K1_AVAILABLE: + # Use secp256k1 library + privkey = PrivateKey(self.private_key) + self.public_key = privkey.pubkey.serialize()[1:] # Remove 0x04 prefix (uncompressed) + else: + # Simplified fallback (NOT cryptographically secure for production) + self.public_key = hashlib.sha256(self.private_key).digest() + + # Encode as bech32 + self.npub = bech32_encode("npub", self.public_key) + self.nsec = bech32_encode("nsec", self.private_key) + + return self.npub, self.nsec + + def get_public_key_hex(self) -> str: + """Get public key as hex string""" + if self.public_key: + return self.public_key.hex() + return "" + + def get_private_key_hex(self) -> str: + """Get private key as hex string""" + if self.private_key: + return self.private_key.hex() + return "" + + def sign_event(self, event_id: str) -> str: + """Sign event with Schnorr signature""" + if not self.private_key: + raise ValueError("Private key not available") + + event_id_bytes = bytes.fromhex(event_id) + + if SECP256K1_AVAILABLE: + privkey = PrivateKey(self.private_key) + # Schnorr signature (BIP-340) + signature = privkey.schnorr_sign(event_id_bytes, None, raw=True) + return signature.hex() + else: + # Simplified fallback (NOT secure for production) + signature = hashlib.sha256(self.private_key + event_id_bytes).digest() + return signature.hex() + + def to_psinet_did(self) -> str: + """Convert Nostr public key to PsiNet DID""" + if not self.public_key: + raise ValueError("Public key not available") + + # Create DID from Nostr public key + pubkey_hash = hashlib.sha256(self.public_key).hexdigest()[:16] + return f"did:psinet:nostr:{pubkey_hash}" + + +class NostrClient: + """ + Nostr relay client for publishing and subscribing to events + """ + + def __init__(self, relays: Optional[List[str]] = None): + self.relays = relays or [ + "wss://relay.damus.io", + "wss://relay.nostr.band", + "wss://nos.lol", + "wss://relay.snort.social" + ] + + self.relay_connections: Dict[str, NostrRelay] = {} + + # Initialize relay configurations + for relay_url in self.relays: + self.relay_connections[relay_url] = NostrRelay(url=relay_url) + + def publish_event(self, event: NostrEvent) -> bool: + """ + Publish event to Nostr relays + + In production, use websocket connection: + - Connect to relay via WebSocket + - Send ["EVENT", event.to_dict()] + - Wait for ["OK", event_id, true/false, message] + """ + print(f"πŸ“‘ Publishing to {len(self.relays)} Nostr relays...") + + for relay_url in self.relays: + relay = self.relay_connections[relay_url] + if not relay.write: + continue + + print(f" β†’ {relay_url[:30]}...") + + # In production, use websocket: + # ws = websocket.create_connection(relay_url) + # ws.send(json.dumps(["EVENT", event.to_dict()])) + # response = json.loads(ws.recv()) + # ws.close() + + # Simulated for demo + print(f" βœ“ Published event {event.id[:16]}...") + + return True + + def subscribe(self, filters: Dict[str, Any]) -> List[NostrEvent]: + """ + Subscribe to events from relays + + In production: + - Send ["REQ", subscription_id, filters] + - Receive ["EVENT", subscription_id, event] + - Close with ["CLOSE", subscription_id] + """ + print(f"πŸ“‘ Subscribing to events with filters: {filters}") + + # Simulated for demo + events = [] + print(f" βœ“ Received 0 events (demo mode)") + + return events + + def add_relay(self, relay_url: str, read: bool = True, write: bool = True): + """Add a new relay""" + if relay_url not in self.relay_connections: + self.relays.append(relay_url) + self.relay_connections[relay_url] = NostrRelay( + url=relay_url, + read=read, + write=write + ) + + +class PsiNetNostrBridge: + """ + Bridge between PsiNet and Nostr protocol + + Converts PsiNet contexts to Nostr events and publishes them + to the Nostr network for decentralized distribution. + """ + + def __init__(self, psinet_node, nostr_client: NostrClient): + self.psinet_node = psinet_node + self.nostr_client = nostr_client + self.key_manager = NostrKeyManager() + + # Generate Nostr keys from PsiNet identity + self.npub, self.nsec = self.key_manager.generate_keys() + + def context_to_nostr_event( + self, + context, + kind: NostrEventKind = NostrEventKind.PSINET_CONTEXT + ) -> NostrEvent: + """Convert PsiNet context to Nostr event""" + + # Create tags + tags = [ + ["d", context.id], # 'd' tag for replaceable events + ["psinet", "context"], + ["type", context.type], + ["owner", context.owner] + ] + + if context.previous: + tags.append(["e", context.previous]) # Reference to previous event + + # Content is JSON-encoded context + content = json.dumps({ + "content": context.content, + "metadata": context.metadata, + "timestamp": context.timestamp + }) + + # Create event + created_at = int(time.time()) + pubkey = self.key_manager.get_public_key_hex() + + event_id = NostrEvent.compute_id( + pubkey=pubkey, + created_at=created_at, + kind=kind.value, + tags=tags, + content=content + ) + + # Sign event + signature = self.key_manager.sign_event(event_id) + + event = NostrEvent( + id=event_id, + pubkey=pubkey, + created_at=created_at, + kind=kind.value, + tags=tags, + content=content, + sig=signature + ) + + return event + + def publish_context(self, context) -> NostrEvent: + """Publish PsiNet context to Nostr network""" + + # Determine event kind based on context type + kind_map = { + "conversation": NostrEventKind.PSINET_CONVERSATION, + "memory": NostrEventKind.PSINET_MEMORY, + "skill": NostrEventKind.PSINET_SKILL, + "knowledge": NostrEventKind.PSINET_KNOWLEDGE + } + + kind = kind_map.get(context.type, NostrEventKind.PSINET_CONTEXT) + + # Convert to Nostr event + event = self.context_to_nostr_event(context, kind) + + # Publish to relays + self.nostr_client.publish_event(event) + + print(f"βœ“ Published context to Nostr: {event.id[:16]}...") + return event + + def create_zap_request( + self, + amount_sats: int, + recipient_pubkey: str, + context_id: Optional[str] = None, + comment: str = "" + ) -> NostrEvent: + """ + Create Lightning zap request (NIP-57) + Integrates with X402 payment protocol + """ + + tags = [ + ["p", recipient_pubkey], # Recipient + ["amount", str(amount_sats * 1000)], # Amount in millisats + ["relays"] + self.nostr_client.relays[:3] # Relay hints + ] + + if context_id: + tags.append(["e", context_id]) # Context being paid for + + content = comment + + created_at = int(time.time()) + pubkey = self.key_manager.get_public_key_hex() + + event_id = NostrEvent.compute_id( + pubkey=pubkey, + created_at=created_at, + kind=NostrEventKind.ZAP_REQUEST.value, + tags=tags, + content=content + ) + + signature = self.key_manager.sign_event(event_id) + + event = NostrEvent( + id=event_id, + pubkey=pubkey, + created_at=created_at, + kind=NostrEventKind.ZAP_REQUEST.value, + tags=tags, + content=content, + sig=signature + ) + + return event + + def publish_zap_request( + self, + amount_sats: int, + recipient_pubkey: str, + context_id: Optional[str] = None, + comment: str = "" + ) -> NostrEvent: + """Publish zap request to Nostr (for Lightning payment)""" + + event = self.create_zap_request( + amount_sats=amount_sats, + recipient_pubkey=recipient_pubkey, + context_id=context_id, + comment=comment + ) + + self.nostr_client.publish_event(event) + + print(f"⚑ Published zap request: {amount_sats} sats") + return event + + def create_text_note(self, content: str, reply_to: Optional[str] = None) -> NostrEvent: + """Create a simple text note (kind 1)""" + + tags = [] + if reply_to: + tags.append(["e", reply_to, "", "reply"]) + + created_at = int(time.time()) + pubkey = self.key_manager.get_public_key_hex() + + event_id = NostrEvent.compute_id( + pubkey=pubkey, + created_at=created_at, + kind=NostrEventKind.TEXT_NOTE.value, + tags=tags, + content=content + ) + + signature = self.key_manager.sign_event(event_id) + + event = NostrEvent( + id=event_id, + pubkey=pubkey, + created_at=created_at, + kind=NostrEventKind.TEXT_NOTE.value, + tags=tags, + content=content, + sig=signature + ) + + return event + + def get_nostr_profile(self) -> Dict[str, Any]: + """Get Nostr profile information""" + return { + "npub": self.npub, + "pubkey": self.key_manager.get_public_key_hex(), + "psinet_did": self.key_manager.to_psinet_did(), + "relays": self.nostr_client.relays + } + + +# Helper functions + +def create_nostr_metadata(name: str, about: str, picture: str = "") -> Dict[str, str]: + """Create Nostr metadata (kind 0)""" + return { + "name": name, + "about": about, + "picture": picture + } + + +if __name__ == "__main__": + print("Ξ¨Net Nostr Integration - Use examples/demo_nostr.py") diff --git a/src/python/psinet_payment.py b/src/python/psinet_payment.py new file mode 100644 index 0000000..b0001b7 --- /dev/null +++ b/src/python/psinet_payment.py @@ -0,0 +1,532 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net X402 Payment Protocol Extension +===================================== + +Implements HTTP 402 "Payment Required" protocol for monetizing AI context access. + +Features: +- Cryptocurrency payment support (Bitcoin, Ethereum, Lightning) +- Pay-per-context and pay-per-query models +- Payment verification and receipts +- Payment channels for micropayments +- Integration with DID-based identity +- Automatic payment enforcement + +Author: PsiNet Protocol Team +License: MIT +""" + +import json +import time +import hashlib +import base64 +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Any +from datetime import datetime, timedelta +from enum import Enum +from decimal import Decimal + + +class PaymentMethod(Enum): + """Supported payment methods""" + BITCOIN = "bitcoin" + ETHEREUM = "ethereum" + LIGHTNING = "lightning" + CUSTOM_TOKEN = "custom_token" + ARWEAVE_AR = "arweave_ar" + IPFS_FILECOIN = "ipfs_filecoin" + + +class PaymentStatus(Enum): + """Payment status states""" + PENDING = "pending" + CONFIRMED = "confirmed" + FAILED = "failed" + EXPIRED = "expired" + REFUNDED = "refunded" + + +class PricingModel(Enum): + """Pricing models for context access""" + FREE = "free" + PAY_PER_ACCESS = "pay_per_access" + PAY_PER_QUERY = "pay_per_query" + SUBSCRIPTION = "subscription" + PAY_PER_TOKEN = "pay_per_token" # For AI token usage + AUCTION = "auction" # Highest bidder gets access + + +@dataclass +class PaymentRequirement: + """Defines payment requirements for accessing a context""" + pricing_model: str # PricingModel + amount: str # Decimal as string (e.g., "0.001") + currency: str # PaymentMethod + recipient_address: str # Wallet address + expires: Optional[str] = None # ISO timestamp + description: Optional[str] = None + metadata: Optional[Dict[str, Any]] = None + + def to_dict(self) -> Dict: + return asdict(self) + + def is_expired(self) -> bool: + if not self.expires: + return False + expires_dt = datetime.fromisoformat(self.expires.replace('Z', '')) + return datetime.utcnow() > expires_dt + + +@dataclass +class PaymentReceipt: + """Payment receipt proving payment was made""" + receipt_id: str + payment_requirement_id: str + payer_did: str + recipient_did: str + amount: str + currency: str + transaction_hash: str # Blockchain transaction hash + status: str # PaymentStatus + timestamp: str + context_id: Optional[str] = None + signature: Optional[str] = None + metadata: Optional[Dict[str, Any]] = None + + def to_dict(self) -> Dict: + return asdict(self) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + +@dataclass +class PaymentChannel: + """Lightning-style payment channel for micropayments""" + channel_id: str + payer_did: str + recipient_did: str + total_capacity: str # Total amount deposited + current_balance: str # Remaining balance + currency: str + opened: str + expires: str + status: str # "open", "closed", "disputed" + signature: Optional[str] = None + + def to_dict(self) -> Dict: + return asdict(self) + + def has_sufficient_balance(self, amount: str) -> bool: + return Decimal(self.current_balance) >= Decimal(amount) + + +@dataclass +class X402Response: + """HTTP 402 Payment Required response""" + status_code: int = 402 + message: str = "Payment Required" + payment_requirement: Optional[PaymentRequirement] = None + payment_methods_accepted: Optional[List[str]] = None + payment_endpoint: Optional[str] = None + + def to_dict(self) -> Dict: + return { + "status_code": self.status_code, + "message": self.message, + "payment_requirement": self.payment_requirement.to_dict() if self.payment_requirement else None, + "payment_methods_accepted": self.payment_methods_accepted, + "payment_endpoint": self.payment_endpoint + } + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + +class PaymentVerifier: + """Verifies payments on various blockchains""" + + @staticmethod + def verify_bitcoin_payment(tx_hash: str, expected_amount: str, recipient_address: str) -> bool: + """ + Verify Bitcoin payment (requires blockchain API) + In production, use blockchain.info API or run a Bitcoin node + """ + # Placeholder - would connect to Bitcoin RPC or API + print(f"πŸ” Verifying Bitcoin payment: {tx_hash[:16]}...") + print(f" Expected: {expected_amount} BTC to {recipient_address[:16]}...") + + # Simulate verification + # In production: query blockchain API + return True # Placeholder + + @staticmethod + def verify_ethereum_payment(tx_hash: str, expected_amount: str, recipient_address: str) -> bool: + """ + Verify Ethereum payment (requires Web3 connection) + In production, use Web3.py to connect to Ethereum node + """ + print(f"πŸ” Verifying Ethereum payment: {tx_hash[:16]}...") + print(f" Expected: {expected_amount} ETH to {recipient_address[:16]}...") + + # Placeholder - would use Web3.py + return True # Placeholder + + @staticmethod + def verify_lightning_payment(payment_hash: str, expected_amount: str) -> bool: + """ + Verify Lightning Network payment (requires LND connection) + In production, connect to LND node + """ + print(f"πŸ” Verifying Lightning payment: {payment_hash[:16]}...") + print(f" Expected: {expected_amount} satoshis") + + # Placeholder - would connect to LND + return True # Placeholder + + @staticmethod + def verify_arweave_payment(tx_id: str, expected_amount: str) -> bool: + """Verify Arweave payment""" + print(f"πŸ” Verifying Arweave payment: {tx_id[:16]}...") + print(f" Expected: {expected_amount} AR") + + return True # Placeholder + + +class PaymentManager: + """ + Manages payments for PsiNet contexts + + Implements the X402 protocol for monetizing AI context access. + """ + + def __init__(self, node_did: str, storage_dir: str = ".psinet/payments"): + self.node_did = node_did + self.storage_dir = storage_dir + + # Storage + self.payment_requirements: Dict[str, PaymentRequirement] = {} + self.payment_receipts: Dict[str, PaymentReceipt] = {} + self.payment_channels: Dict[str, PaymentChannel] = {} + + # Configuration + self.wallet_addresses: Dict[str, str] = { + "bitcoin": "", + "ethereum": "", + "lightning": "" + } + + def create_payment_requirement( + self, + context_id: str, + pricing_model: PricingModel, + amount: str, + currency: PaymentMethod, + recipient_address: str, + expires_in_hours: Optional[int] = 24, + description: Optional[str] = None + ) -> PaymentRequirement: + """Create a payment requirement for a context""" + + expires = None + if expires_in_hours: + expires_dt = datetime.utcnow() + timedelta(hours=expires_in_hours) + expires = expires_dt.isoformat() + "Z" + + requirement = PaymentRequirement( + pricing_model=pricing_model.value, + amount=amount, + currency=currency.value, + recipient_address=recipient_address, + expires=expires, + description=description or f"Payment for context {context_id[:16]}", + metadata={"context_id": context_id} + ) + + self.payment_requirements[context_id] = requirement + return requirement + + def create_payment_receipt( + self, + payment_requirement: PaymentRequirement, + payer_did: str, + transaction_hash: str, + context_id: Optional[str] = None + ) -> PaymentReceipt: + """Create a payment receipt after successful payment""" + + receipt_id = hashlib.sha256( + f"{payer_did}:{transaction_hash}:{time.time()}".encode() + ).hexdigest() + + receipt = PaymentReceipt( + receipt_id=receipt_id, + payment_requirement_id=id(payment_requirement), + payer_did=payer_did, + recipient_did=self.node_did, + amount=payment_requirement.amount, + currency=payment_requirement.currency, + transaction_hash=transaction_hash, + status=PaymentStatus.PENDING.value, + timestamp=datetime.utcnow().isoformat() + "Z", + context_id=context_id + ) + + self.payment_receipts[receipt_id] = receipt + return receipt + + def verify_payment(self, receipt: PaymentReceipt) -> bool: + """Verify a payment receipt against blockchain""" + + currency = receipt.currency + tx_hash = receipt.transaction_hash + amount = receipt.amount + + # Get payment requirement to check recipient + requirement = None + for req in self.payment_requirements.values(): + if req.currency == currency: + requirement = req + break + + if not requirement: + print(f"❌ No payment requirement found for {currency}") + return False + + # Verify based on currency + verified = False + if currency == PaymentMethod.BITCOIN.value: + verified = PaymentVerifier.verify_bitcoin_payment( + tx_hash, amount, requirement.recipient_address + ) + elif currency == PaymentMethod.ETHEREUM.value: + verified = PaymentVerifier.verify_ethereum_payment( + tx_hash, amount, requirement.recipient_address + ) + elif currency == PaymentMethod.LIGHTNING.value: + verified = PaymentVerifier.verify_lightning_payment( + tx_hash, amount + ) + elif currency == PaymentMethod.ARWEAVE_AR.value: + verified = PaymentVerifier.verify_arweave_payment( + tx_hash, amount + ) + + # Update receipt status + if verified: + receipt.status = PaymentStatus.CONFIRMED.value + print(f"βœ“ Payment verified: {receipt.receipt_id[:16]}") + else: + receipt.status = PaymentStatus.FAILED.value + print(f"❌ Payment verification failed: {receipt.receipt_id[:16]}") + + return verified + + def check_payment_for_context( + self, + context_id: str, + requester_did: str + ) -> Optional[X402Response]: + """ + Check if payment is required for context access + Returns X402Response if payment required, None if access granted + """ + + # Check if context has payment requirement + if context_id not in self.payment_requirements: + return None # Free access + + requirement = self.payment_requirements[context_id] + + # Check if payment requirement expired + if requirement.is_expired(): + return None # Free access after expiration + + # Check if requester already paid + for receipt in self.payment_receipts.values(): + if (receipt.context_id == context_id and + receipt.payer_did == requester_did and + receipt.status == PaymentStatus.CONFIRMED.value): + return None # Already paid + + # Check payment channels + for channel in self.payment_channels.values(): + if (channel.payer_did == requester_did and + channel.recipient_did == self.node_did and + channel.status == "open" and + channel.has_sufficient_balance(requirement.amount)): + # Deduct from channel + channel.current_balance = str( + Decimal(channel.current_balance) - Decimal(requirement.amount) + ) + print(f"βœ“ Paid via payment channel: {requirement.amount} {requirement.currency}") + return None # Access granted + + # Payment required - return 402 response + return X402Response( + status_code=402, + message="Payment Required", + payment_requirement=requirement, + payment_methods_accepted=[ + PaymentMethod.BITCOIN.value, + PaymentMethod.ETHEREUM.value, + PaymentMethod.LIGHTNING.value + ], + payment_endpoint=f"psinet://payments/{self.node_did}" + ) + + def open_payment_channel( + self, + payer_did: str, + total_capacity: str, + currency: PaymentMethod, + expires_in_days: int = 30 + ) -> PaymentChannel: + """Open a Lightning-style payment channel for micropayments""" + + channel_id = hashlib.sha256( + f"{payer_did}:{self.node_did}:{time.time()}".encode() + ).hexdigest() + + expires_dt = datetime.utcnow() + timedelta(days=expires_in_days) + + channel = PaymentChannel( + channel_id=channel_id, + payer_did=payer_did, + recipient_did=self.node_did, + total_capacity=total_capacity, + current_balance=total_capacity, + currency=currency.value, + opened=datetime.utcnow().isoformat() + "Z", + expires=expires_dt.isoformat() + "Z", + status="open" + ) + + self.payment_channels[channel_id] = channel + print(f"βœ“ Payment channel opened: {channel_id[:16]}") + print(f" Capacity: {total_capacity} {currency.value}") + + return channel + + def close_payment_channel(self, channel_id: str): + """Close a payment channel""" + if channel_id in self.payment_channels: + channel = self.payment_channels[channel_id] + channel.status = "closed" + print(f"βœ“ Payment channel closed: {channel_id[:16]}") + print(f" Remaining balance: {channel.current_balance}") + + def generate_payment_invoice( + self, + context_id: str, + amount: str, + currency: PaymentMethod, + description: str + ) -> Dict[str, Any]: + """Generate a payment invoice""" + + invoice_id = hashlib.sha256( + f"{context_id}:{amount}:{time.time()}".encode() + ).hexdigest() + + recipient_address = self.wallet_addresses.get(currency.value, "") + + invoice = { + "invoice_id": invoice_id, + "context_id": context_id, + "amount": amount, + "currency": currency.value, + "recipient_address": recipient_address, + "recipient_did": self.node_did, + "description": description, + "created": datetime.utcnow().isoformat() + "Z", + "qr_code_data": self._generate_payment_uri( + currency, recipient_address, amount + ) + } + + return invoice + + def _generate_payment_uri( + self, + currency: PaymentMethod, + address: str, + amount: str + ) -> str: + """Generate payment URI for QR codes""" + + if currency == PaymentMethod.BITCOIN: + return f"bitcoin:{address}?amount={amount}" + elif currency == PaymentMethod.ETHEREUM: + return f"ethereum:{address}?value={amount}" + elif currency == PaymentMethod.LIGHTNING: + return f"lightning:{address}?amount={amount}" + else: + return f"{currency.value}:{address}?amount={amount}" + + def get_pricing_stats(self) -> Dict[str, Any]: + """Get payment and pricing statistics""" + + total_receipts = len(self.payment_receipts) + confirmed_payments = sum( + 1 for r in self.payment_receipts.values() + if r.status == PaymentStatus.CONFIRMED.value + ) + + total_revenue = {} + for receipt in self.payment_receipts.values(): + if receipt.status == PaymentStatus.CONFIRMED.value: + currency = receipt.currency + amount = Decimal(receipt.amount) + total_revenue[currency] = total_revenue.get(currency, Decimal(0)) + amount + + return { + "total_receipts": total_receipts, + "confirmed_payments": confirmed_payments, + "pending_payments": total_receipts - confirmed_payments, + "total_revenue": {k: str(v) for k, v in total_revenue.items()}, + "open_channels": len([c for c in self.payment_channels.values() if c.status == "open"]), + "monetized_contexts": len(self.payment_requirements) + } + + +# Helper functions for integration + +def create_paid_context(node, payment_manager, context_data, price, currency): + """Helper: Create a context with payment requirement""" + from psinet_simple import ContextType + + # Create context + context = node.create_context( + context_type=ContextType.CONVERSATION, + content=context_data + ) + + # Add payment requirement + requirement = payment_manager.create_payment_requirement( + context_id=context.id, + pricing_model=PricingModel.PAY_PER_ACCESS, + amount=price, + currency=currency, + recipient_address=payment_manager.wallet_addresses.get(currency.value, "demo-address") + ) + + return context, requirement + + +def access_paid_context(payment_manager, context_id, requester_did): + """Helper: Check payment and access context""" + + response = payment_manager.check_payment_for_context(context_id, requester_did) + + if response: + # Payment required + return False, response + else: + # Access granted + return True, None + + +if __name__ == "__main__": + print("Ξ¨Net X402 Payment Protocol - Use examples/demo_payment.py") diff --git a/src/python/psinet_simple.py b/src/python/psinet_simple.py new file mode 100644 index 0000000..21cccd5 --- /dev/null +++ b/src/python/psinet_simple.py @@ -0,0 +1,257 @@ +#!/usr/bin/env python3 +""" +Ξ¨Net: Simplified Implementation (No Cryptography Dependencies) +================================================================ + +A simplified version of PsiNet that works without cryptography libraries. +Uses hashlib for basic signing (NOT cryptographically secure - for demo only). + +For production use, install cryptography and use psinet_core.py instead. + +Author: PsiNet Protocol Team +License: MIT +""" + +import json +import time +import hashlib +import base64 +import os +from dataclasses import dataclass, asdict +from typing import Dict, List, Optional, Any +from datetime import datetime, timedelta +from enum import Enum +from pathlib import Path + + +class ContextType(Enum): + """Types of contexts that can be stored""" + CONVERSATION = "conversation" + MEMORY = "memory" + SKILL = "skill" + KNOWLEDGE = "knowledge" + DOCUMENT = "document" + EMBEDDING = "embedding" + + +class AccessCapability(Enum): + """Access control capabilities""" + READ = "read" + WRITE = "write" + SHARE = "share" + DELEGATE = "delegate" + ADMIN = "admin" + + +@dataclass +class DIDDocument: + """Decentralized Identifier Document""" + did: str + public_key: str + created: str + updated: str + service_endpoints: List[str] + verification_methods: List[Dict[str, str]] + + def to_dict(self) -> Dict: + return asdict(self) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + +@dataclass +class ContextUnit: + """A single unit of context (like a blockchain block)""" + id: str + type: str + content: Dict[str, Any] + owner: str + previous: Optional[str] + timestamp: str + signature: Optional[str] = None + metadata: Optional[Dict[str, Any]] = None + storage_refs: Optional[Dict[str, str]] = None + + def to_dict(self) -> Dict: + return asdict(self) + + def to_json(self) -> str: + return json.dumps(self.to_dict(), indent=2) + + def content_hash(self) -> str: + """Generate content-addressed hash""" + data = json.dumps({ + "type": self.type, + "content": self.content, + "owner": self.owner, + "previous": self.previous, + "timestamp": self.timestamp + }, sort_keys=True) + return hashlib.sha256(data.encode()).hexdigest() + + +class SimplePsiNetNode: + """ + Simplified PsiNet Node (Demo Mode - Not Cryptographically Secure) + + Uses basic hashing instead of Ed25519 signatures. + For production, use psinet_core.py with cryptography library. + """ + + def __init__(self, storage_dir: str = ".psinet"): + self.storage_dir = Path(storage_dir) + self.storage_dir.mkdir(exist_ok=True) + + self.private_key: Optional[str] = None + self.public_key: Optional[str] = None + self.did: Optional[str] = None + self.did_document: Optional[DIDDocument] = None + + self.contexts: Dict[str, ContextUnit] = {} + + self._init_storage() + + def _init_storage(self): + """Initialize storage directories""" + (self.storage_dir / "contexts").mkdir(exist_ok=True) + (self.storage_dir / "dids").mkdir(exist_ok=True) + (self.storage_dir / "keys").mkdir(exist_ok=True) + + def generate_identity(self) -> DIDDocument: + """Generate a new DID (simplified - uses random bytes instead of Ed25519)""" + # Generate random "keypair" (NOT cryptographically secure) + import secrets + random_bytes = secrets.token_bytes(32) + + self.private_key = base64.b64encode(random_bytes).decode() + self.public_key = hashlib.sha256(random_bytes).hexdigest() + + # Create DID + did_suffix = self.public_key[:16] + self.did = f"did:psinet:{did_suffix}" + + # Create DID Document + now = datetime.utcnow().isoformat() + "Z" + self.did_document = DIDDocument( + did=self.did, + public_key=self.public_key, + created=now, + updated=now, + service_endpoints=[], + verification_methods=[{ + "id": f"{self.did}#keys-1", + "type": "SimplifiedKey", + "controller": self.did, + "publicKeyHex": self.public_key + }] + ) + + # Save to disk + self._save_did_document() + self._save_private_key() + + print("⚠️ Note: Using simplified cryptography (demo mode)") + print(" For production, install 'cryptography' package") + + return self.did_document + + def _save_did_document(self): + """Save DID document to disk""" + if not self.did_document: + return + + did_file = self.storage_dir / "dids" / f"{self.did.replace(':', '_')}.json" + with open(did_file, 'w') as f: + f.write(self.did_document.to_json()) + + def _save_private_key(self): + """Save private key to disk""" + if not self.private_key: + return + + key_file = self.storage_dir / "keys" / f"{self.did.replace(':', '_')}.key" + with open(key_file, 'w') as f: + f.write(self.private_key) + + os.chmod(key_file, 0o600) + + def create_context( + self, + context_type: ContextType, + content: Dict[str, Any], + previous: Optional[str] = None, + metadata: Optional[Dict[str, Any]] = None + ) -> ContextUnit: + """Create a new context unit""" + + if not self.did: + raise RuntimeError("Identity required. Call generate_identity() first.") + + timestamp = datetime.utcnow().isoformat() + "Z" + + context = ContextUnit( + id="", + type=context_type.value, + content=content, + owner=self.did, + previous=previous, + timestamp=timestamp, + metadata=metadata or {}, + storage_refs={} + ) + + # Generate content-addressed ID + context.id = context.content_hash() + + # "Sign" the context (simplified - just hash with private key) + if self.private_key: + signature_data = f"{context.id}:{self.private_key}".encode() + context.signature = hashlib.sha256(signature_data).hexdigest() + + # Store + self.contexts[context.id] = context + self._save_context(context) + + return context + + def _save_context(self, context: ContextUnit): + """Save context to disk""" + context_file = self.storage_dir / "contexts" / f"{context.id}.json" + with open(context_file, 'w') as f: + f.write(context.to_json()) + + def query_contexts( + self, + context_type: Optional[ContextType] = None, + owner: Optional[str] = None, + limit: int = 10 + ) -> List[ContextUnit]: + """Query contexts with filters""" + results = [] + + for context in self.contexts.values(): + if context_type and context.type != context_type.value: + continue + if owner and context.owner != owner: + continue + + results.append(context) + + if len(results) >= limit: + break + + return results + + def get_stats(self) -> Dict[str, Any]: + """Get node statistics""" + return { + "did": self.did, + "contexts_count": len(self.contexts), + "storage_dir": str(self.storage_dir), + "mode": "simplified (demo)" + } + + +if __name__ == "__main__": + print("Ξ¨Net Simple Library - Use examples/demo_simple.py to see it in action")