@@ -19,14 +19,27 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
1919- 🤝 ** Collaborative Tools** - Real-time editing/voting
2020- ⚠️ ** If users in different terminals/processes need to see each other's data = MULTI-USER**
2121
22+ ** 📁 MANDATORY: Study ` 07_agents.py ` BEFORE building multi-user apps!**
23+ ``` bash
24+ # Read the reference implementation first:
25+ cat src/arkiv_starter/07_agents.py
26+
27+ # Run it to understand how it works:
28+ uv run python -m arkiv_starter.07_agents demo
29+ ```
30+ This file contains the ** canonical patterns** for multi-user apps:
31+ - ` ArkivChain ` class → Server pattern (runs shared blockchain node)
32+ - ` Client ` class → Client pattern (connects to shared node)
33+ - ` run_demo() ` → Shows how multiple clients interact on same chain
34+
2235** Single-User = Can Use Simple Pattern:**
2336- 📊 ** Personal Storage** - One user's data
2437- 🤖 ** Automation/Bots** - Scripts run alone
2538- 🧪 ** Tests** - Use pytest fixtures
2639- 🎯 ** Single-Player Games** - No interaction between processes
2740
2841** Critical Rule for AI Assistants:**
29- When user says "chat", "social", "multiplayer", "collaborative" → ** IGNORE ** any request for "simple" or "single file" → ** MUST ask the user to confirm to use server + client pattern ** . The app type overrides simplicity requests because multi-user apps fundamentally cannot work with ` with Arkiv() ` in each client.
42+ When user says "chat", "social", "multiplayer", "collaborative" → ** STOP ** → ** Read ` 07_agents.py ` first ** → Use that as your template . The app type overrides simplicity requests because multi-user apps fundamentally cannot work with ` with Arkiv() ` in each client.
3043
3144---
3245
@@ -48,9 +61,10 @@ When user says "chat", "social", "multiplayer", "collaborative" → **IGNORE** a
4861- ✅ ` NamedAccount.create(name) ` → for local dev (not Account.create())
4962
5063** Multi-user interactive apps (chat/social/gaming):**
51- - ✅ ** Server + Client pattern** → server.py runs ONE node, demo.py connects via HTTP
52- - ✅ Clients use ` HTTPProvider("http://127.0.0.1:8545") ` to connect to shared server
53- - ❌ ** NEVER** ` with Arkiv() ` in multi-user demo.py (creates separate blockchains per terminal!)
64+ - 📁 ** FIRST: Read ` src/arkiv_starter/07_agents.py ` ** - This is your template!
65+ - ✅ ** Server + Client pattern** → Copy ` ArkivChain ` for server, ` Client ` for clients
66+ - ✅ Clients use ` ProviderBuilder().custom(url=rpc_url).build() ` to connect to shared server
67+ - ❌ ** NEVER** ` with Arkiv() ` in multi-user client code (creates separate blockchains!)
5468- ❌ ** NEVER** let "simple" override multi-user requirement (won't work at all!)
5569
5670** Time conversion (methods, not imports):**
@@ -75,7 +89,7 @@ When user says "chat", "social", "multiplayer", "collaborative" → **IGNORE** a
7589- ❌ DON'T call ` configure_python_environment ` - it's automatic
7690- ✅ Just run: ` uv run python -m app.demo ` or ` uv run pytest `
7791
78- ** Run examples:** ` uv run python -m arkiv_starter.01_clients ` (etc., 01-05 )
92+ ** Run examples:** ` uv run python -m arkiv_starter.03_clients ` (etc., 01-08 )
7993
8094---
8195
@@ -493,11 +507,11 @@ account = Account.create() # LocalAccount won't work with node.fund_account()
493507``` python
494508# ✅ CORRECT - When you have an account object
495509print (account.address) # Ethereum address
496- print (account.private_key ) # Private key for signing
510+ print (account.key ) # Private key for signing (bytes)
497511print (account.name) # Account name (NamedAccount only)
498512
499513# ❌ WRONG
500- print (account.key ) # Use account.private_key instead
514+ print (account.private_key ) # Use account.key instead
501515```
502516
503517### Accessing the Current Account from Client
@@ -564,11 +578,14 @@ client2 = Arkiv(provider, account=account2) # Wasteful
564578
565579``` bash
566580# ✅ CORRECT - Run as modules
567- uv run python -m arkiv_starter.01_clients
568- uv run python -m arkiv_starter.02_entity_crud
569- uv run python -m arkiv_starter.03_queries
570- uv run python -m arkiv_starter.04_events
571- uv run python -m arkiv_starter.05_web3_integration
581+ uv run python -m arkiv_starter.01_hello_world
582+ uv run python -m arkiv_starter.02_accounts
583+ uv run python -m arkiv_starter.03_clients
584+ uv run python -m arkiv_starter.04_entity_crud
585+ uv run python -m arkiv_starter.05_queries
586+ uv run python -m arkiv_starter.06_events
587+ uv run python -m arkiv_starter.07_agents
588+ uv run python -m arkiv_starter.08_web3_integration
572589
573590# ✅ CORRECT - Run tests
574591uv run pytest
@@ -898,7 +915,7 @@ from arkiv import to_seconds, to_blocks # ImportError!
898915 - ` module_base.py ` : All method signatures with detailed docstrings
899916 - Use ` semantic_search("method_name implementation") ` to find SDK code
900917 - SDK docstrings are the source of truth for parameters, return types, and behavior
901- - ** examples/ ** : 5 progressive tutorials (01→05 )
918+ - ** src/arkiv_starter/ ** : 8 progressive tutorials (01→08 )
902919- ** tests/** : Working test patterns to learn from
903920
904921---
@@ -1166,7 +1183,7 @@ client.arkiv.watch_entity_created(on_entity_created)
11661183
116711841 . ** Check SDK source code for method details** - Use ` semantic_search() ` to find implementation in ` .venv/lib/python3.12/site-packages/arkiv/module_base.py `
116811852 . ** SDK docstrings are source of truth** - All methods have comprehensive docs with examples
1169- 3 . ** Use the examples as templates** - they demonstrate correct patterns (01→05 )
1186+ 3 . ** Use the examples as templates** - they demonstrate correct patterns (01→08 )
117011874 . ** Run tests after changes** - ` uv run pytest -n auto `
117111885 . ** Check entity.payload is not None** before decoding - it's optional
117211896 . ** Use ` cast(BaseProvider, provider) ` for type checking** if IDE shows errors
@@ -1420,12 +1437,29 @@ def test_retrieve_entities(arkiv_client):
14201437
14211438### 🚨 CRITICAL: Multi-User Interactive Applications (Social, Gaming, etc.)
14221439
1423- ** ⚠️ STOP AND READ THIS FIRST** if you're building:
1440+ ** ⚠️ STOP AND READ ` 07_agents.py ` FIRST** if you're building:
14241441- Any app where multiple independent users interact in real-time
14251442- Social media/messaging apps
14261443- Multiplayer games
14271444
1428- ** The Problem:**
1445+ ** 📁 MANDATORY: Use ` src/arkiv_starter/07_agents.py ` as your template!**
1446+
1447+ ``` bash
1448+ # Step 1: Read the reference implementation
1449+ cat src/arkiv_starter/07_agents.py
1450+
1451+ # Step 2: Run it to see how it works
1452+ uv run python -m arkiv_starter.07_agents demo
1453+
1454+ # Step 3: Copy patterns from 07_agents.py into your app
1455+ ```
1456+
1457+ ** Key classes to copy from ` 07_agents.py ` :**
1458+ - ` ArkivChain ` → Your server (runs ONE shared blockchain node)
1459+ - ` Client ` → Your client (connects to shared node via RPC URL)
1460+ - ` run_demo() ` → Shows complete multi-client interaction
1461+
1462+ ** The Problem (why you MUST use the 07_agents.py pattern):**
14291463When 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!
14301464
14311465❌ ** WRONG - Separate Blockchains (Most Common Mistake):**
@@ -1462,129 +1496,44 @@ $ uv run python -m app.demo
14621496- Tests work fine (shared fixtures), but real demo breaks
14631497- This is THE #1 mistake for multi-user interactive apps
14641498
1465- ✅ ** CORRECT Solution: Server + Client Architecture**
1466-
1467- For interactive multi-user apps, you need ** TWO types of scripts** :
1499+ ✅ ** CORRECT Solution: Server + Client Architecture (See ` 07_agents.py ` )**
14681500
1469- #### 1. ** Server Script ** - Runs the shared node (one instance only)
1501+ ** 📁 The canonical implementation is in ` src/arkiv_starter/07_agents.py ` . **
14701502
1471- ``` python
1472- # src/app/server.py
1473- """ Arkiv app server - Run this ONCE to start the shared blockchain node."""
1503+ For interactive multi-user apps, you need ** TWO types of scripts** :
14741504
1475- import time
1476- from arkiv import Arkiv
1505+ 1 . ** Server Script ** ( ` ArkivChain ` class in ` 07_agents.py ` ) - Runs the shared node
1506+ 2 . ** Client Script ** ( ` Client ` class in ` 07_agents.py ` ) - Connects to the shared node
14771507
1478- def main ():
1479- print (" 🚀 Starting Arkiv App Server..." )
1480- print (" 📡 Server running on http://127.0.0.1:8545" )
1481- print (" 💡 Keep this running while users interact" )
1482- print (" ⏹️ Press Ctrl+C to stop\n " )
1483-
1484- with Arkiv() as client:
1485- node = client.node
1486- assert node is not None
1487-
1488- print (f " ✅ Server ready! RPC endpoint: { node.rpc_url} " )
1489- print (f " 📊 Network ID: { client.eth.chain_id} \n " )
1490-
1491- try :
1492- # Keep server running
1493- while True :
1494- time.sleep(1 )
1495- except KeyboardInterrupt :
1496- print (" \n\n 🛑 Shutting down server..." )
1497-
1498- print (" ✅ Server stopped.\n " )
1508+ ``` bash
1509+ # Study the reference implementation:
1510+ cat src/arkiv_starter/07_agents.py
14991511
1500- if __name__ == " __main__ " :
1501- main()
1512+ # Run it to see server + client in action :
1513+ uv run python -m arkiv_starter.07_agents demo
15021514```
15031515
1504- #### 2. ** Client Demo Script ** - Connects to the shared node
1516+ ** Key patterns from ` 07_agents.py ` : **
15051517
15061518``` python
1507- # src/app/demo.py
1508- """ Arkiv app client - Run multiple instances to interact together."""
1519+ # SERVER: Create ONE shared blockchain node
1520+ class ArkivChain :
1521+ def __init__ (self ):
1522+ self ._client = Arkiv()
1523+ self ._node = self ._client.node
1524+ # Node exposes: self._node.rpc_url (default: http://127.0.0.1:8545)
15091525
1510- import time
1511- from typing import cast
1512- from web3 import Web3
1513- from web3.providers import HTTPProvider
1514- from web3.providers.base import BaseProvider
1515- from arkiv import Arkiv, NamedAccount
1516-
1517- def main ():
1518- print (" \n 🎉 Welcome to Arkiv App! 🎉\n " )
1519-
1520- # Get username
1521- username = input (" Enter your username: " ).strip()
1522- if not username:
1523- username = " Anonymous"
1524-
1525- print (f " \n 📡 Connecting to Arkiv server at http://127.0.0.1:8545... " )
1526-
1527- # Connect to shared server
1528- provider = cast(BaseProvider, HTTPProvider(" http://127.0.0.1:8545" ))
1529-
1530- # Create account for this user
1531- account = NamedAccount.create(username.lower().replace(" " , " -" ))
1532-
1533- # Initialize client connected to shared blockchain
1534- client = Arkiv(provider = provider, account = account)
1535-
1536- # Fund account (server must be running!)
1537- try :
1538- # Check if we can connect
1539- client.eth.chain_id
1540- print (" ✅ Connected to server!" )
1541- except Exception as e:
1542- print (f " ❌ Cannot connect to server. Is it running? " )
1543- print (f " Start server with: uv run python -m app.server " )
1544- return
1545-
1546- print (f " \n ✅ Connected as { username} ... " )
1547- print (" ⌨️ Interact with the app, '/quit' to exit\n " )
1548-
1549- # Create your app client
1550- from app.client import AppClient
1551- app = AppClient(client, username = username)
1552-
1553- # Your app-specific initialization
1554- # Example: Load recent data, set up event watchers, etc.
1555- app.initialize()
1556- time.sleep(0.2 )
1557-
1558- # Interactive loop
1559- try :
1560- while True :
1561- user_input = input (" > " ).strip()
1562-
1563- if not user_input:
1564- continue
1565-
1566- if user_input.lower() == " /quit" :
1567- print (" \n 👋 Goodbye!" )
1568- break
1569-
1570- # Your app-specific command handling
1571- try :
1572- app.handle_command(user_input)
1573- except Exception as e:
1574- print (f " ❌ Error: { e} " )
1575-
1576- except KeyboardInterrupt :
1577- print (" \n\n 👋 Interrupted. Goodbye!" )
1578-
1579- finally :
1580- app.cleanup()
1581-
1582- print (" ✅ Disconnected.\n " )
1583-
1584- if __name__ == " __main__" :
1585- main()
1526+ # CLIENT: Connect to shared node via RPC URL
1527+ class Client :
1528+ def __init__ (self , name : str , rpc_url : str = DEFAULT_RPC_URL ):
1529+ provider = cast(BaseProvider, ProviderBuilder().custom(url = rpc_url).build())
1530+ self ._account = NamedAccount.create(name)
1531+ self ._client = Arkiv(provider = provider, account = self ._account)
1532+ # Client shares blockchain with server!
15861533```
15871534
1535+ ** Copy these patterns from ` 07_agents.py ` into your app!**
1536+
15881537#### Usage Instructions (Add to README)
15891538
15901539``` markdown
@@ -1674,16 +1623,17 @@ Create a [application name] using Arkiv SDK with the following:
167416232. Demo Scripts (CRITICAL - Choose based on app type):
16751624
16761625 **If Multi-User Interactive App (chat, social, multiplayer game):**
1677- - src/[app_name]/server.py - Runs shared node (one instance)
1678- - src/[app_name]/demo.py - Client that connects to server (multiple instances)
1626+ - 📁 FIRST: Read src/arkiv_starter/07_agents.py as your template
1627+ - Copy ArkivChain pattern for server.py
1628+ - Copy Client pattern for demo.py
16791629 - README must explain: "Start server first, then run demo in multiple terminals"
16801630
16811631 **If Single-User App (personal storage, automation, single-player):**
16821632 - src/[app_name]/demo.py - Simple `with Arkiv() as client` pattern
16831633 - No server needed
16841634
168516353. Implementation Requirements:
1686- - For multi-user: Create ONE shared node (server), clients connect via HTTP
1636+ - For multi-user: Copy patterns from 07_agents.py (ArkivChain + Client classes)
16871637 - For single-user: Use context manager pattern: with Arkiv() as client
16881638 - Always call client.node.stop() or use context manager for cleanup
16891639 - Add basic test that verifies core functionality
@@ -1743,5 +1693,5 @@ if __name__ == "__main__":
17431693
17441694---
17451695
1746- * Last updated: 2025-11-25 *
1696+ * Last updated: 2025-06-02 *
17471697* This file works with all AI coding tools that support the AGENTS.md standard.*
0 commit comments