@@ -25,6 +25,12 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
2525- ✅ ` client.current_signer ` → track current account name for switching back
2626- ✅ ` NamedAccount.create(name) ` → for local dev (not Account.create())
2727
28+ ** Multi-user interactive apps (chat/social/gaming):**
29+ - ✅ ** Server + Client pattern** → server.py runs ONE node, demo.py connects via HTTP
30+ - ✅ Clients use ` HTTPProvider("http://127.0.0.1:8545") ` to connect to shared server
31+ - ⚠️ ** ` client = Arkiv() ` is fine for prototyping/testing** but NOT for multi-user/multi-process apps
32+ - ❌ ** NOT** ` with Arkiv() ` in demo.py (creates separate blockchains per terminal!)
33+
2834** Time conversion (methods, not imports):**
2935- ✅ ` client.arkiv.to_seconds(days=7) ` → method on arkiv module
3036- ✅ ` client.arkiv.to_blocks(days=1) ` → method on arkiv module
@@ -1347,33 +1353,272 @@ def main():
13471353** Always create a basic test** to verify your core functionality works:
13481354
13491355``` python
1350- # tests/test_chat .py
1356+ # tests/test_app .py
13511357import pytest
1352- from chat .client import ChatClient
1358+ from app .client import AppClient
13531359
1354- def test_send_message ( client ):
1355- """ Test that messages can be sent successfully."""
1356- chat = ChatClient( client = client )
1360+ def test_create_entity ( arkiv_client ):
1361+ """ Test that entities can be created successfully."""
1362+ app = AppClient(arkiv_client )
13571363
1358- message_key = chat.send_message (" Test message " )
1364+ entity_key = app.create_item (" Test data " )
13591365
1360- assert message_key is not None
1361- assert client .arkiv.entity_exists(message_key )
1366+ assert entity_key is not None
1367+ assert arkiv_client .arkiv.entity_exists(entity_key )
13621368
1363- def test_retrieve_messages ( client ):
1364- """ Test that messages can be retrieved."""
1365- chat = ChatClient( client = client )
1369+ def test_retrieve_entities ( arkiv_client ):
1370+ """ Test that entities can be retrieved."""
1371+ app = AppClient(arkiv_client )
13661372
1367- # Send a message
1368- chat.send_message (" Test message " )
1373+ # Create an entity
1374+ app.create_item (" Test data " )
13691375
1370- # Retrieve messages
1371- messages = chat.get_messages ()
1376+ # Retrieve entities
1377+ items = app.get_items ()
13721378
1373- assert len (messages) > 0
1374- assert messages[0 ][" text" ] == " Test message"
1379+ assert len (items) > 0
1380+ ```
1381+
1382+ ### 🚨 CRITICAL: Multi-User Interactive Applications (Social, Gaming, etc.)
1383+
1384+ ** ⚠️ STOP AND READ THIS FIRST** if you're building:
1385+ - Any app where multiple independent users interact in real-time
1386+ - Social media/messaging apps
1387+ - Multiplayer games
1388+
1389+ ** The Problem:**
1390+ When you run a demo script like ` python -m app.demo ` in multiple terminals, each creates its ** own separate blockchain** . Users can't see each other!
1391+
1392+ ❌ ** WRONG - Separate Blockchains (Most Common Mistake):**
1393+
1394+ ``` python
1395+ # src/app/demo.py
1396+ def main ():
1397+ with Arkiv() as client: # ← Creates NEW blockchain every time!
1398+ app = AppClient(client, username = username)
1399+ # ... user interacts but is alone in their own universe
1400+
1401+ if __name__ == " __main__" :
1402+ main()
1403+ ```
1404+
1405+ ** What happens:**
1406+ ``` bash
1407+ # Terminal 1
1408+ $ uv run python -m app.demo
1409+ # Enter username: Alice
1410+ # ← Alice creates Blockchain A
1411+
1412+ # Terminal 2
1413+ $ uv run python -m app.demo
1414+ # Enter username: Bob
1415+ # ← Bob creates Blockchain B (completely separate!)
1416+
1417+ # Alice and Bob CANNOT see each other's data!
13751418```
13761419
1420+ ** Why this is tricky:**
1421+ - Each ` Arkiv() ` call starts a new blockchain node
1422+ - Different processes = different blockchains
1423+ - Tests work fine (shared fixtures), but real demo breaks
1424+ - This is THE #1 mistake for multi-user interactive apps
1425+
1426+ ✅ ** CORRECT Solution: Server + Client Architecture**
1427+
1428+ For interactive multi-user apps, you need ** TWO types of scripts** :
1429+
1430+ #### 1. ** Server Script** - Runs the shared node (one instance only)
1431+
1432+ ``` python
1433+ # src/app/server.py
1434+ """ Arkiv app server - Run this ONCE to start the shared blockchain node."""
1435+
1436+ import time
1437+ from arkiv import Arkiv
1438+
1439+ def main ():
1440+ print (" 🚀 Starting Arkiv App Server..." )
1441+ print (" 📡 Server running on http://127.0.0.1:8545" )
1442+ print (" 💡 Keep this running while users interact" )
1443+ print (" ⏹️ Press Ctrl+C to stop\n " )
1444+
1445+ with Arkiv() as client:
1446+ node = client.node
1447+ assert node is not None
1448+
1449+ print (f " ✅ Server ready! RPC endpoint: { node.rpc_url} " )
1450+ print (f " 📊 Network ID: { client.eth.chain_id} \n " )
1451+
1452+ try :
1453+ # Keep server running
1454+ while True :
1455+ time.sleep(1 )
1456+ except KeyboardInterrupt :
1457+ print (" \n\n 🛑 Shutting down server..." )
1458+
1459+ print (" ✅ Server stopped.\n " )
1460+
1461+ if __name__ == " __main__" :
1462+ main()
1463+ ```
1464+
1465+ #### 2. ** Client Demo Script** - Connects to the shared node
1466+
1467+ ``` python
1468+ # src/app/demo.py
1469+ """ Arkiv app client - Run multiple instances to interact together."""
1470+
1471+ import time
1472+ from typing import cast
1473+ from web3 import Web3
1474+ from web3.providers import HTTPProvider
1475+ from web3.providers.base import BaseProvider
1476+ from arkiv import Arkiv, NamedAccount
1477+
1478+ def main ():
1479+ print (" \n 🎉 Welcome to Arkiv App! 🎉\n " )
1480+
1481+ # Get username
1482+ username = input (" Enter your username: " ).strip()
1483+ if not username:
1484+ username = " Anonymous"
1485+
1486+ print (f " \n 📡 Connecting to Arkiv server at http://127.0.0.1:8545... " )
1487+
1488+ # Connect to shared server
1489+ provider = cast(BaseProvider, HTTPProvider(" http://127.0.0.1:8545" ))
1490+
1491+ # Create account for this user
1492+ account = NamedAccount.create(username.lower().replace(" " , " -" ))
1493+
1494+ # Initialize client connected to shared blockchain
1495+ client = Arkiv(provider = provider, account = account)
1496+
1497+ # Fund account (server must be running!)
1498+ try :
1499+ # Check if we can connect
1500+ client.eth.chain_id
1501+ print (" ✅ Connected to server!" )
1502+ except Exception as e:
1503+ print (f " ❌ Cannot connect to server. Is it running? " )
1504+ print (f " Start server with: uv run python -m app.server " )
1505+ return
1506+
1507+ print (f " \n ✅ Connected as { username} ... " )
1508+ print (" ⌨️ Interact with the app, '/quit' to exit\n " )
1509+
1510+ # Create your app client
1511+ from app.client import AppClient
1512+ app = AppClient(client, username = username)
1513+
1514+ # Your app-specific initialization
1515+ # Example: Load recent data, set up event watchers, etc.
1516+ app.initialize()
1517+ time.sleep(0.2 )
1518+
1519+ # Interactive loop
1520+ try :
1521+ while True :
1522+ user_input = input (" > " ).strip()
1523+
1524+ if not user_input:
1525+ continue
1526+
1527+ if user_input.lower() == " /quit" :
1528+ print (" \n 👋 Goodbye!" )
1529+ break
1530+
1531+ # Your app-specific command handling
1532+ try :
1533+ app.handle_command(user_input)
1534+ except Exception as e:
1535+ print (f " ❌ Error: { e} " )
1536+
1537+ except KeyboardInterrupt :
1538+ print (" \n\n 👋 Interrupted. Goodbye!" )
1539+
1540+ finally :
1541+ app.cleanup()
1542+
1543+ print (" ✅ Disconnected.\n " )
1544+
1545+ if __name__ == " __main__" :
1546+ main()
1547+ ```
1548+
1549+ #### Usage Instructions (Add to README)
1550+
1551+ ``` markdown
1552+ ## Multi-User Demo
1553+
1554+ ### Step 1: Start the server (ONCE, in one terminal)
1555+
1556+ ``` bash
1557+ uv run python -m app.server
1558+ ```
1559+
1560+ Keep this running! It hosts the shared blockchain.
1561+
1562+ ### Step 2: Connect clients (multiple terminals)
1563+
1564+ ** Terminal 2:**
1565+ ``` bash
1566+ uv run python -m app.demo
1567+ # Enter username: Alice
1568+ ```
1569+
1570+ ** Terminal 3:**
1571+ ``` bash
1572+ uv run python -m app.demo
1573+ # Enter username: Bob
1574+ ```
1575+
1576+ ** Terminal 4:**
1577+ ``` bash
1578+ uv run python -m app.demo
1579+ # Enter username: Charlie
1580+ ```
1581+
1582+ Now Alice, Bob, and Charlie can all interact together in real-time! 🎉
1583+ ```
1584+
1585+ #### Key Implementation Details
1586+
1587+ ** Server Requirements:**
1588+ - ✅ Creates and keeps the Arkiv node running
1589+ - ✅ Exposes RPC endpoint (default: http://127.0.0.1:8545 )
1590+ - ✅ Stays alive until Ctrl+C
1591+ - ✅ Only ONE instance should run
1592+
1593+ ** Client Requirements:**
1594+ - ✅ Connects to existing server via HTTP provider
1595+ - ✅ Creates unique account per user
1596+ - ✅ Funds account from server (or pre-fund accounts)
1597+ - ✅ Multiple instances can run simultaneously
1598+ - ✅ Each user has their own client but shares the blockchain
1599+
1600+ ** Account Funding:**
1601+ - Server node can fund accounts
1602+ - Or pre-create funded accounts in server startup
1603+ - Or use a faucet pattern (server endpoint to fund accounts)
1604+
1605+ ### When to Use This Pattern
1606+
1607+ ** Multi-User Interactive Apps (ALWAYS use server + client):**
1608+ - ✅ Chat/messaging applications (multiple users communicating)
1609+ - ✅ Social media apps (users posting/commenting/liking)
1610+ - ✅ Multiplayer games (players interacting in shared world)
1611+ - ✅ Collaborative tools (real-time editing/voting/planning)
1612+ - ✅ Any app where independent processes need to see each other's data
1613+ - ✅ Any CLI demo where you say "open multiple terminals"
1614+
1615+ ** Single-User or Testing (can use simple ` with Arkiv() ` ):**
1616+ - ✅ Personal data storage demos
1617+ - ✅ Automated scripts (cron jobs, bots)
1618+ - ✅ pytest tests (use fixtures)
1619+ - ✅ Single-player games
1620+ - ✅ Data import/export tools
1621+
13771622### Complete AI Prompt Pattern
13781623
13791624Use this template when asking AI to create a new application:
@@ -1384,23 +1629,34 @@ Create a [application name] using Arkiv SDK with the following:
138416291. Project Structure:
13851630 - Core code in src/[app_name]/ with __init__.py
13861631 - Main class in src/[app_name]/client.py
1387- - Demo script in src/[app_name]/demo.py that IMPORTS and USES the main class
13881632 - Tests in tests/test_[app_name].py
1389-
1390- 2. Implementation Requirements:
1391- - Use context manager pattern: with Arkiv() as client
1392- - For multi-user: Create ONE shared node, multiple clients with shared provider
1633+ - Demo scripts (see #2 below)
1634+
1635+ 2. Demo Scripts (CRITICAL - Choose based on app type):
1636+
1637+ **If Multi-User Interactive App (chat, social, multiplayer game):**
1638+ - src/[app_name]/server.py - Runs shared node (one instance)
1639+ - src/[app_name]/demo.py - Client that connects to server (multiple instances)
1640+ - README must explain: "Start server first, then run demo in multiple terminals"
1641+
1642+ **If Single-User App (personal storage, automation, single-player):**
1643+ - src/[app_name]/demo.py - Simple `with Arkiv() as client` pattern
1644+ - No server needed
1645+
1646+ 3. Implementation Requirements:
1647+ - For multi-user: Create ONE shared node (server), clients connect via HTTP
1648+ - For single-user: Use context manager pattern: with Arkiv() as client
13931649 - Always call client.node.stop() or use context manager for cleanup
13941650 - Add basic test that verifies core functionality
13951651
1396- 3 . Code Organization:
1652+ 4 . Code Organization:
13971653 - Demo script should NOT duplicate core functionality
13981654 - Demo should import from the main module
13991655 - Include docstrings for all public methods
14001656
1401- 4 . Testing:
1657+ 5 . Testing:
14021658 - Create at least 2 tests: one for creation, one for retrieval
1403- - Use fixtures from conftest.py
1659+ - Use fixtures from conftest.py (tests ALWAYS use shared fixtures)
14041660 - Run with: uv run pytest tests/test_[app_name].py
14051661```
14061662
0 commit comments