@@ -29,6 +29,190 @@ This file provides context for AI coding tools (GitHub Copilot, Cursor, Aider, G
2929
3030---
3131
32+ ## 🏗️ Critical: Understanding Arkiv Architecture
33+
34+ ### Client/Node Architecture
35+
36+ ** ⚠️ MOST IMPORTANT CONCEPT** : Arkiv uses a ** Client → Node → Blockchain Network** architecture.
37+
38+ ```
39+ LOCAL DEVELOPMENT (Single Node):
40+ ┌─────────────┐ ┌─────────────────────────────┐
41+ │ Client │ ───→ │ Arkiv Node │
42+ │ (Arkiv) │ │ (Full Blockchain Instance) │
43+ └─────────────┘ └─────────────────────────────┘
44+
45+ PRODUCTION (Multi-Node Network):
46+ ┌────────────────────────────────────────────────────────────────┐
47+ │ BLOCKCHAIN (Network of nodes). │
48+ ┌─────────────┐ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
49+ │ Client │ ───→ │ │ Arkiv Node 1 │ ←──→ │ Arkiv Node 2 │ ←──→ │ Arkiv Node 3 │ │
50+ │ (Arkiv) │ │ │ (Consensus) │ │ (Consensus) │ │ (Consensus) │ │
51+ └─────────────┘ │ └──────────────┘ └──────────────┘ └──────────────┘ │
52+ │ Shared Ledger (Replicated State) │
53+ └────────────────────────────────────────────────────────────────┘
54+ ```
55+
56+ ** Key Understanding:**
57+ - ** Client** = Python object that sends transactions/queries (lightweight)
58+ - ** Arkiv Node** = Full blockchain node running consensus, storage, and networking (heavyweight)
59+ - ** Local Node** = Complete, real blockchain node - NOT a simulation or mock
60+ - ** Production Network** = Multiple nodes running the same software, forming consensus
61+
62+ ** Critical Insight:**
63+ Your local ` ArkivNode() ` is ** identical** to production nodes - same code, same blockchain logic, same storage. The only difference is that it runs ** alone** (single node) instead of in a ** network** (multiple nodes forming consensus). This means:
64+ - ✅ Your local tests are running against ** real blockchain code**
65+ - ✅ Behavior is identical to production (just without network consensus)
66+ - ✅ When you deploy, you're connecting to multiple copies of what you tested locally
67+
68+ ** For Clients to Interact:**
69+ - ⚠️ ** Multiple clients MUST connect to the SAME node/blockchain**
70+ - ⚠️ Each ` Arkiv() ` creates a separate blockchain - clients on different nodes CANNOT see each other's data
71+ - ✅ Use shared node pattern (shown below) for multi-client communication
72+
73+ ### Each ` Arkiv() ` Creates a NEW Node!
74+
75+ ❌ ** CRITICAL MISTAKE - Multiple Isolated Blockchains:**
76+
77+ ``` python
78+ # This creates TWO separate blockchains that CANNOT communicate!
79+ client1 = Arkiv() # Starts Node 1 with Blockchain A
80+ client2 = Arkiv() # Starts Node 2 with Blockchain B (completely separate!)
81+
82+ # Alice and Bob are in different universes - no communication possible!
83+ client1.arkiv.create_entity(... ) # Stored in Blockchain A
84+ client2.arkiv.create_entity(... ) # Stored in Blockchain B
85+ ```
86+
87+ ** Why this happens:**
88+ - ` Arkiv() ` without parameters calls ` ArkivNode().start() ` internally
89+ - Each node runs its own blockchain (like starting separate PostgreSQL instances)
90+ - Coming from traditional APIs (where multiple clients → one server), this is counterintuitive
91+
92+ ✅ ** CORRECT Pattern 1 - Single Client, Multiple Accounts (Testing/Simulation Only):**
93+
94+ ``` python
95+ # Use when ONE entity needs to sign transactions as DIFFERENT accounts
96+ # Example: Testing ownership transfers, simulating multi-user scenarios
97+ client = Arkiv() # One node, one blockchain
98+
99+ # Create multiple accounts
100+ alice = NamedAccount.create(" alice" )
101+ bob = NamedAccount.create(" bob" )
102+ client.node.fund_account(alice)
103+ client.node.fund_account(bob)
104+
105+ # Add to client registry
106+ client.accounts[" alice" ] = alice
107+ client.accounts[" bob" ] = bob
108+
109+ # Switch signing account (same client controls both)
110+ client.switch_to(" alice" )
111+ client.arkiv.create_entity(... ) # Alice's message
112+
113+ client.switch_to(" bob" )
114+ client.arkiv.create_entity(... ) # Bob's message (same blockchain!)
115+ ```
116+
117+ ⚠️ ** Important** : Pattern 1 is for single-client scenarios (tests, demos, automation). When simulating real multi-party interaction, use Pattern 2.
118+
119+ ✅ ** CORRECT Pattern 2 - Multiple Independent Clients, Shared Node (Multi-Party Interaction):**
120+
121+ ``` python
122+ # Use when DIFFERENT parties need to interact with each other
123+ # Each party maintains their own client and private key
124+ # Critical: Parties MUST NOT share accounts - each has independent client
125+
126+ # Create ONE shared node
127+ main_client = Arkiv() # Starts the node
128+ shared_node = main_client.node
129+
130+ # Create provider from shared node
131+ from arkiv.provider import ProviderBuilder
132+ from web3.providers.base import BaseProvider
133+ from typing import cast
134+
135+ provider = cast(BaseProvider, ProviderBuilder().node(shared_node).build())
136+
137+ # Each party gets their own client with their own account
138+ alice_account = NamedAccount.create(" alice" )
139+ bob_account = NamedAccount.create(" bob" )
140+ shared_node.fund_account(alice_account)
141+ shared_node.fund_account(bob_account)
142+
143+ alice_client = Arkiv(provider = provider, account = alice_account)
144+ bob_client = Arkiv(provider = provider, account = bob_account)
145+
146+ # Now Alice and Bob interact independently on the SAME blockchain
147+ alice_client.arkiv.create_entity(... ) # Alice's action (Alice's private key)
148+ bob_client.arkiv.create_entity(... ) # Bob's action (Bob's private key)
149+ # They can see each other's data because they share the blockchain!
150+ ```
151+
152+ ** Key Difference:**
153+ - ** Pattern 1** : ONE client switches between accounts (for testing/automation)
154+ - ** Pattern 2** : MULTIPLE independent clients, each with own account (for real multi-party interaction)
155+
156+ ### Node Lifecycle Management
157+
158+ ** ⚠️ CRITICAL** : Nodes are long-running processes that MUST be cleaned up.
159+
160+ ❌ ** WRONG - Resource Leak:**
161+
162+ ``` python
163+ def my_function ():
164+ client = Arkiv() # Starts node
165+ client.arkiv.create_entity(... )
166+ # Function ends, node keeps running! Memory leak!
167+ ```
168+
169+ You'll see this warning:
170+ ```
171+ Arkiv client with managed node is being destroyed but node is still running.
172+ Call arkiv.node.stop() or use context manager: 'with Arkiv() as arkiv:'
173+ ```
174+
175+ ✅ ** CORRECT Pattern 1 - Context Manager (Recommended):**
176+
177+ ``` python
178+ # Node automatically stopped when exiting context
179+ with Arkiv() as client:
180+ client.arkiv.create_entity(... )
181+ # ... do work ...
182+ # Node stopped here automatically
183+ ```
184+
185+ ✅ ** CORRECT Pattern 2 - Explicit Cleanup:**
186+
187+ ``` python
188+ client = Arkiv()
189+ try :
190+ client.arkiv.create_entity(... )
191+ # ... do work ...
192+ finally :
193+ if client.node:
194+ client.node.stop() # Explicitly stop node
195+ ```
196+
197+ ✅ ** CORRECT Pattern 3 - Shared Node in Threads:**
198+
199+ ``` python
200+ # Main thread owns the node
201+ main_client = Arkiv()
202+
203+ def worker_thread (shared_node ):
204+ provider = ProviderBuilder().node(shared_node).build()
205+ client = Arkiv(provider = provider, account = my_account)
206+ # Do work...
207+ client.arkiv.cleanup_filters() # Clean up event watchers
208+ # DON'T call client.node.stop() - main thread owns the node!
209+
210+ # Later, in main thread:
211+ main_client.node.stop() # Only the owner stops the node
212+ ```
213+
214+ ---
215+
32216## 🌟 What is Arkiv? (For New AI Assistants)
33217
34218** Arkiv is a Web3 database that solves the Web3 data trilemma: Decentralization + Queryability + Simplicity.**
@@ -889,5 +1073,191 @@ if len(payload) > 90_000: # ~90KB safe limit
8891073
8901074---
8911075
892- * Last updated: 2024-11-25*
1076+ ## 🤖 AI-Generated Project Guidelines
1077+
1078+ When creating new applications using AI assistants (Copilot, Cursor, etc.), follow these critical patterns:
1079+
1080+ ### Project Structure
1081+
1082+ ** ⚠️ IMPORTANT** : User code goes in ` src/ ` , NOT in ` src/arkiv_starter/ `
1083+
1084+ ```
1085+ src/
1086+ ├── arkiv_starter/ # Template examples (READ-ONLY reference)
1087+ │ ├── 01_clients.py
1088+ │ ├── 02_entity_crud.py
1089+ │ └── ...
1090+ │
1091+ ├── my_app/ # ✅ Your application code here
1092+ │ ├── __init__.py
1093+ │ ├── core.py # Core functionality
1094+ │ ├── models.py # Data models
1095+ │ └── utils.py # Helper functions
1096+ │
1097+ ├── my_app_demo.py # ✅ Demo script (imports from my_app/)
1098+ └── tests/
1099+ └── test_my_app.py # ✅ Tests for your app
1100+ ```
1101+
1102+ ❌ ** WRONG - Polluting template examples:**
1103+ ``` python
1104+ # DON'T add your code to arkiv_starter/
1105+ src/ arkiv_starter/ my_chat_app.py # Wrong location!
1106+ ```
1107+
1108+ ✅ ** CORRECT - Separate user code:**
1109+ ``` python
1110+ # Put your code in its own module
1111+ src/ chat/
1112+ ├── __init__ .py
1113+ ├── client.py # Chat client class
1114+ ├── messages.py # Message handling
1115+ └── demo.py # Demo script
1116+ ```
1117+
1118+ ### Demo vs. Core Code Separation
1119+
1120+ ** Key principle:** Demo code should USE your core functionality, not REPLICATE it.
1121+
1122+ ❌ ** WRONG - Code duplication:**
1123+ ``` python
1124+ # chat_demo.py - DON'T do this
1125+ def send_message (client , text ): # Reimplementing functionality
1126+ # ... duplicate code ...
1127+
1128+ def watch_messages (client ): # Reimplementing functionality
1129+ # ... duplicate code ...
1130+
1131+ # Demo uses duplicated code
1132+ send_message(client, " Hello" )
1133+ ```
1134+
1135+ ✅ ** CORRECT - Demo imports and uses core code:**
1136+ ``` python
1137+ # src/chat/client.py - Core functionality
1138+ class ChatClient :
1139+ def send_message (self , text : str ):
1140+ # Implementation here
1141+ pass
1142+
1143+ def watch_messages (self ):
1144+ # Implementation here
1145+ pass
1146+
1147+ # src/chat/demo.py - Demo uses core code
1148+ from chat.client import ChatClient
1149+
1150+ def main ():
1151+ chat = ChatClient()
1152+ chat.send_message(" Hello" )
1153+ chat.watch_messages()
1154+ ```
1155+
1156+ ### Initial Testing
1157+
1158+ ** Always create a basic test** to verify your core functionality works:
1159+
1160+ ``` python
1161+ # tests/test_chat.py
1162+ import pytest
1163+ from chat.client import ChatClient
1164+
1165+ def test_send_message (client ):
1166+ """ Test that messages can be sent successfully."""
1167+ chat = ChatClient(client = client)
1168+
1169+ message_key = chat.send_message(" Test message" )
1170+
1171+ assert message_key is not None
1172+ assert client.arkiv.entity_exists(message_key)
1173+
1174+ def test_retrieve_messages (client ):
1175+ """ Test that messages can be retrieved."""
1176+ chat = ChatClient(client = client)
1177+
1178+ # Send a message
1179+ chat.send_message(" Test message" )
1180+
1181+ # Retrieve messages
1182+ messages = chat.get_messages()
1183+
1184+ assert len (messages) > 0
1185+ assert messages[0 ][" text" ] == " Test message"
1186+ ```
1187+
1188+ ### Complete AI Prompt Pattern
1189+
1190+ Use this template when asking AI to create a new application:
1191+
1192+ ```
1193+ Create a [application name] using Arkiv SDK with the following:
1194+
1195+ 1. Project Structure:
1196+ - Core code in src/[app_name]/ with __init__.py
1197+ - Main class in src/[app_name]/client.py
1198+ - Demo script in src/[app_name]/demo.py that IMPORTS and USES the main class
1199+ - Tests in tests/test_[app_name].py
1200+
1201+ 2. Implementation Requirements:
1202+ - Use context manager pattern: with Arkiv() as client
1203+ - For multi-user: Create ONE shared node, multiple clients with shared provider
1204+ - Always call client.node.stop() or use context manager for cleanup
1205+ - Add basic test that verifies core functionality
1206+
1207+ 3. Code Organization:
1208+ - Demo script should NOT duplicate core functionality
1209+ - Demo should import from the main module
1210+ - Include docstrings for all public methods
1211+
1212+ 4. Testing:
1213+ - Create at least 2 tests: one for creation, one for retrieval
1214+ - Use fixtures from conftest.py
1215+ - Run with: uv run pytest tests/test_[app_name].py
1216+ ```
1217+
1218+ ### Example: Good Project Creation
1219+
1220+ ** User prompt:**
1221+ ```
1222+ Create a voting application using Arkiv. Follow the AI-Generated Project Guidelines
1223+ in AGENTS.md. Include core functionality in src/voting/, demo in src/voting/demo.py,
1224+ and tests in tests/test_voting.py.
1225+ ```
1226+
1227+ ** Expected AI output:**
1228+ ```
1229+ src/voting/
1230+ ├── __init__.py
1231+ ├── poll.py # Poll class with create_poll, vote, get_results
1232+ ├── demo.py # Demo that imports Poll and shows usage
1233+ tests/
1234+ └── test_voting.py # Tests for Poll functionality
1235+ ```
1236+
1237+ ** Demo pattern:**
1238+ ``` python
1239+ # src/voting/demo.py
1240+ from voting.poll import Poll
1241+
1242+ def main ():
1243+ with Arkiv() as client:
1244+ poll = Poll(client)
1245+
1246+ # Create poll
1247+ poll_id = poll.create(" Favorite color?" , [" Red" , " Blue" , " Green" ])
1248+
1249+ # Vote
1250+ poll.vote(poll_id, " Blue" )
1251+
1252+ # Show results
1253+ results = poll.get_results(poll_id)
1254+ print (results)
1255+
1256+ if __name__ == " __main__" :
1257+ main()
1258+ ```
1259+
1260+ ---
1261+
1262+ * Last updated: 2025-11-25*
8931263* This file works with all AI coding tools that support the AGENTS.md standard.*
0 commit comments