neuronum 8.4.0__tar.gz → 10.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of neuronum might be problematic. Click here for more details.

@@ -0,0 +1,157 @@
1
+ Metadata-Version: 2.4
2
+ Name: neuronum
3
+ Version: 10.0.0
4
+ Summary: An E2EE Data Engine
5
+ Home-page: https://neuronum.net
6
+ Author: Neuronum Cybernetics
7
+ Author-email: welcome@neuronum.net
8
+ Project-URL: GitHub, https://github.com/neuronumcybernetics/neuronum
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE.md
15
+ Requires-Dist: aiohttp
16
+ Requires-Dist: aiofiles
17
+ Requires-Dist: websockets
18
+ Requires-Dist: click
19
+ Requires-Dist: questionary
20
+ Requires-Dist: python-dotenv
21
+ Requires-Dist: requests
22
+ Requires-Dist: cryptography
23
+ Requires-Dist: bip_utils
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: classifier
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: license-file
31
+ Dynamic: project-url
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
35
+
36
+ <h1 align="center">
37
+ <img src="https://neuronum.net/static/neuronum.svg" alt="Neuronum" width="80">
38
+ </h1>
39
+ <h4 align="center">Neuronum: An E2EE Data Engine</h4>
40
+
41
+ <p align="center">
42
+ <a href="https://neuronum.net">
43
+ <img src="https://img.shields.io/badge/Website-Neuronum-blue" alt="Website">
44
+ </a>
45
+ <a href="https://github.com/neuronumcybernetics/neuronum">
46
+ <img src="https://img.shields.io/badge/Docs-Read%20now-green" alt="Documentation">
47
+ </a>
48
+ <a href="https://pypi.org/project/neuronum/">
49
+ <img src="https://img.shields.io/pypi/v/neuronum.svg" alt="PyPI Version">
50
+ </a><br>
51
+ <img src="https://img.shields.io/badge/Python-3.8%2B-yellow" alt="Python Version">
52
+ <a href="https://github.com/neuronumcybernetics/neuronum/blob/main/LICENSE.md">
53
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
54
+ </a>
55
+ </p>
56
+
57
+ ------------------
58
+
59
+ ### **Getting Started into the Neuronum Network**
60
+ In this brief getting started guide, you will:
61
+ - [Learn about Neuronum](#about-neuronum)
62
+ - [Connect to the Network](#connect-to-neuronum)
63
+ - [Transmit Data Securely](#transmit-data-securely)
64
+ - [Receive Data Securely](#receive-data-securely)
65
+
66
+ ------------------
67
+
68
+ ### **About Neuronum**
69
+ Neuronum is a real-time, end-to-end encrypted data engine built in Python. It enables secure communication between devices and services by encrypting data client-side using the recipient's public key. Encrypted messages are transmitted through a passive relay server and decrypted on the recipient’s device using its private key. The relay server facilitates connectivity but cannot access or alter the content of messages.
70
+
71
+ ### Requirements
72
+ - Python >= 3.8
73
+
74
+ ------------------
75
+
76
+ ### **Connect To Neuronum**
77
+ Installation (optional but recommended: create a virtual environment)
78
+ ```sh
79
+ pip install neuronum
80
+ ```
81
+
82
+ Create your Cell (your secure identity):
83
+ ```sh
84
+ neuronum create-cell
85
+ ```
86
+
87
+ or
88
+
89
+ Connect an existing Cell (your secure identity):
90
+ ```sh
91
+ neuronum connect-cell
92
+ ```
93
+
94
+ ------------------
95
+
96
+
97
+ ### **Transmit Data Securely**
98
+ ```python
99
+ import asyncio
100
+ from neuronum import Cell
101
+
102
+ async def main():
103
+
104
+ async with Cell() as cell:
105
+
106
+ data = {
107
+ "some": "data" # Replace with your actual payload
108
+ }
109
+
110
+ # Use activate_tx() if you expect a response from the other cell
111
+ tx_response = await cell.activate_tx("id::cell", data)
112
+ print(tx_response)
113
+
114
+ # Stream data to another cell (no response expected)
115
+ await cell.stream("id::cell", data)
116
+
117
+ if __name__ == '__main__':
118
+ asyncio.run(main())
119
+ ```
120
+
121
+
122
+ ### **Receive Data Securely**
123
+ ```python
124
+ import asyncio
125
+ from neuronum import Cell
126
+
127
+ async def main():
128
+ async with Cell() as cell:
129
+
130
+ async for transmitter in cell.sync():
131
+ ts_str = transmitter.get("time")
132
+ data = transmitter.get("data")
133
+ transmitter_id = transmitter.get("transmitter_id")
134
+ client_public_key = data.get("public_key", {})
135
+
136
+ response_data = {
137
+ "json": "Data Received Securely - Your request was processed successfully",
138
+ "html": """
139
+ <!DOCTYPE html>
140
+ <html lang="en">
141
+ <head>
142
+ <meta charset="UTF-8">
143
+ <title>Secure Data Response</title>
144
+ </head>
145
+ <body>
146
+ <h3>Data Received Securely</h3>
147
+ <p>Your request was processed successfully.</p>
148
+ </body>
149
+ </html>
150
+ """
151
+ }
152
+ await cell.tx_response(transmitter_id, response_data, client_public_key)
153
+
154
+ if __name__ == '__main__':
155
+ asyncio.run(main())
156
+ ```
157
+
@@ -0,0 +1,122 @@
1
+ <h1 align="center">
2
+ <img src="https://neuronum.net/static/neuronum.svg" alt="Neuronum" width="80">
3
+ </h1>
4
+ <h4 align="center">Neuronum: An E2EE Data Engine</h4>
5
+
6
+ <p align="center">
7
+ <a href="https://neuronum.net">
8
+ <img src="https://img.shields.io/badge/Website-Neuronum-blue" alt="Website">
9
+ </a>
10
+ <a href="https://github.com/neuronumcybernetics/neuronum">
11
+ <img src="https://img.shields.io/badge/Docs-Read%20now-green" alt="Documentation">
12
+ </a>
13
+ <a href="https://pypi.org/project/neuronum/">
14
+ <img src="https://img.shields.io/pypi/v/neuronum.svg" alt="PyPI Version">
15
+ </a><br>
16
+ <img src="https://img.shields.io/badge/Python-3.8%2B-yellow" alt="Python Version">
17
+ <a href="https://github.com/neuronumcybernetics/neuronum/blob/main/LICENSE.md">
18
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License">
19
+ </a>
20
+ </p>
21
+
22
+ ------------------
23
+
24
+ ### **Getting Started into the Neuronum Network**
25
+ In this brief getting started guide, you will:
26
+ - [Learn about Neuronum](#about-neuronum)
27
+ - [Connect to the Network](#connect-to-neuronum)
28
+ - [Transmit Data Securely](#transmit-data-securely)
29
+ - [Receive Data Securely](#receive-data-securely)
30
+
31
+ ------------------
32
+
33
+ ### **About Neuronum**
34
+ Neuronum is a real-time, end-to-end encrypted data engine built in Python. It enables secure communication between devices and services by encrypting data client-side using the recipient's public key. Encrypted messages are transmitted through a passive relay server and decrypted on the recipient’s device using its private key. The relay server facilitates connectivity but cannot access or alter the content of messages.
35
+
36
+ ### Requirements
37
+ - Python >= 3.8
38
+
39
+ ------------------
40
+
41
+ ### **Connect To Neuronum**
42
+ Installation (optional but recommended: create a virtual environment)
43
+ ```sh
44
+ pip install neuronum
45
+ ```
46
+
47
+ Create your Cell (your secure identity):
48
+ ```sh
49
+ neuronum create-cell
50
+ ```
51
+
52
+ or
53
+
54
+ Connect an existing Cell (your secure identity):
55
+ ```sh
56
+ neuronum connect-cell
57
+ ```
58
+
59
+ ------------------
60
+
61
+
62
+ ### **Transmit Data Securely**
63
+ ```python
64
+ import asyncio
65
+ from neuronum import Cell
66
+
67
+ async def main():
68
+
69
+ async with Cell() as cell:
70
+
71
+ data = {
72
+ "some": "data" # Replace with your actual payload
73
+ }
74
+
75
+ # Use activate_tx() if you expect a response from the other cell
76
+ tx_response = await cell.activate_tx("id::cell", data)
77
+ print(tx_response)
78
+
79
+ # Stream data to another cell (no response expected)
80
+ await cell.stream("id::cell", data)
81
+
82
+ if __name__ == '__main__':
83
+ asyncio.run(main())
84
+ ```
85
+
86
+
87
+ ### **Receive Data Securely**
88
+ ```python
89
+ import asyncio
90
+ from neuronum import Cell
91
+
92
+ async def main():
93
+ async with Cell() as cell:
94
+
95
+ async for transmitter in cell.sync():
96
+ ts_str = transmitter.get("time")
97
+ data = transmitter.get("data")
98
+ transmitter_id = transmitter.get("transmitter_id")
99
+ client_public_key = data.get("public_key", {})
100
+
101
+ response_data = {
102
+ "json": "Data Received Securely - Your request was processed successfully",
103
+ "html": """
104
+ <!DOCTYPE html>
105
+ <html lang="en">
106
+ <head>
107
+ <meta charset="UTF-8">
108
+ <title>Secure Data Response</title>
109
+ </head>
110
+ <body>
111
+ <h3>Data Received Securely</h3>
112
+ <p>Your request was processed successfully.</p>
113
+ </body>
114
+ </html>
115
+ """
116
+ }
117
+ await cell.tx_response(transmitter_id, response_data, client_public_key)
118
+
119
+ if __name__ == '__main__':
120
+ asyncio.run(main())
121
+ ```
122
+
@@ -0,0 +1,319 @@
1
+ import click
2
+ import questionary
3
+ from pathlib import Path
4
+ import requests
5
+ from cryptography.hazmat.primitives import hashes
6
+ from cryptography.hazmat.primitives.asymmetric import ec
7
+ from cryptography.hazmat.primitives import serialization
8
+ from cryptography.hazmat.backends import default_backend
9
+ from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateKey
10
+ import base64
11
+ import time
12
+ import hashlib
13
+ from bip_utils import Bip39MnemonicGenerator, Bip39SeedGenerator
14
+ from bip_utils import Bip39MnemonicValidator, Bip39Languages
15
+
16
+ # --- Configuration Constants ---
17
+ NEURONUM_PATH = Path.home() / ".neuronum"
18
+ ENV_FILE = NEURONUM_PATH / ".env"
19
+ PUBLIC_KEY_FILE = NEURONUM_PATH / "public_key.pem"
20
+ PRIVATE_KEY_FILE = NEURONUM_PATH / "private_key.pem"
21
+ API_BASE_URL = "https://neuronum.net/api"
22
+
23
+ # --- Utility Functions ---
24
+
25
+ def sign_message(private_key: EllipticCurvePrivateKey, message: bytes) -> str:
26
+ """Signs a message using the given private key and returns a base64 encoded signature."""
27
+ try:
28
+ signature = private_key.sign(
29
+ message,
30
+ ec.ECDSA(hashes.SHA256())
31
+ )
32
+ return base64.b64encode(signature).decode()
33
+ except Exception as e:
34
+ click.echo(f"❌ Error signing message: {e}")
35
+ return ""
36
+
37
+ def derive_keys_from_mnemonic(mnemonic: str):
38
+ """Derives EC-SECP256R1 keys from a BIP-39 mnemonic's seed."""
39
+ try:
40
+ seed = Bip39SeedGenerator(mnemonic).Generate()
41
+ # Hash the seed to get a deterministic and strong key derivation input
42
+ digest = hashlib.sha256(seed).digest()
43
+ int_key = int.from_bytes(digest, "big")
44
+
45
+ # Derive the private key
46
+ private_key = ec.derive_private_key(int_key, ec.SECP256R1(), default_backend())
47
+ public_key = private_key.public_key()
48
+
49
+ # Serialize keys to PEM format
50
+ pem_private = private_key.private_bytes(
51
+ encoding=serialization.Encoding.PEM,
52
+ format=serialization.PrivateFormat.PKCS8,
53
+ encryption_algorithm=serialization.NoEncryption()
54
+ )
55
+
56
+ pem_public = public_key.public_bytes(
57
+ encoding=serialization.Encoding.PEM,
58
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
59
+ )
60
+
61
+ return private_key, public_key, pem_private, pem_public
62
+
63
+ except Exception as e:
64
+ click.echo(f"❌ Error generating keys from mnemonic: {e}")
65
+ return None, None, None, None
66
+
67
+
68
+ def save_credentials(host: str, mnemonic: str, pem_public: bytes, pem_private: bytes):
69
+ """Saves host, mnemonic, and keys to the .neuronum directory."""
70
+ try:
71
+ NEURONUM_PATH.mkdir(parents=True, exist_ok=True)
72
+
73
+ # Save .env with host and mnemonic (Sensitive data)
74
+ env_content = f"HOST={host}\nMNEMONIC=\"{mnemonic}\""
75
+ ENV_FILE.write_text(env_content)
76
+
77
+ # Save PEM files
78
+ PUBLIC_KEY_FILE.write_bytes(pem_public)
79
+ PRIVATE_KEY_FILE.write_bytes(pem_private)
80
+
81
+ return True
82
+ except Exception as e:
83
+ click.echo(f"❌ Error saving credentials: {e}")
84
+ return False
85
+
86
+ def load_credentials():
87
+ """Loads host, mnemonic, and private key from local files."""
88
+ credentials = {}
89
+ try:
90
+ # Load .env data (Host and Mnemonic)
91
+ if not ENV_FILE.exists():
92
+ click.echo("Error: No credentials found. Please create or connect a cell first.")
93
+ return None
94
+
95
+ with open(ENV_FILE, "r") as f:
96
+ for line in f:
97
+ line = line.strip()
98
+ if "=" in line:
99
+ key, value = line.split("=", 1)
100
+ # Clean up quotes from mnemonic
101
+ credentials[key] = value.strip().strip('"')
102
+
103
+ credentials['host'] = credentials.get("HOST")
104
+ credentials['mnemonic'] = credentials.get("MNEMONIC")
105
+
106
+ # Load Private Key
107
+ with open(PRIVATE_KEY_FILE, "rb") as key_file:
108
+ private_key = serialization.load_pem_private_key(
109
+ key_file.read(),
110
+ password=None,
111
+ backend=default_backend()
112
+ )
113
+ credentials['private_key'] = private_key
114
+ credentials['public_key'] = private_key.public_key()
115
+
116
+ return credentials
117
+
118
+ except FileNotFoundError:
119
+ click.echo("Error: Credentials files are incomplete. Try deleting the '.neuronum' folder or reconnecting.")
120
+ return None
121
+ except Exception as e:
122
+ click.echo(f"Error loading credentials: {e}")
123
+ return None
124
+
125
+ # --- CLI Group ---
126
+
127
+ @click.group()
128
+ def cli():
129
+ """Neuronum CLI Tool for Community Cell management."""
130
+ pass
131
+
132
+ # --- CLI Commands ---
133
+
134
+ @click.command()
135
+ def create_cell():
136
+ """Creates a new Community Cell with a randomly generated key pair."""
137
+
138
+ # 1. Generate Mnemonic and Keys
139
+ mnemonic = Bip39MnemonicGenerator().FromWordsNumber(12)
140
+ private_key, public_key, pem_private, pem_public = derive_keys_from_mnemonic(mnemonic)
141
+
142
+ if not private_key:
143
+ return
144
+
145
+ # 2. Call API to Create Cell
146
+ click.echo("🔗 Requesting new cell creation from server...")
147
+ url = f"{API_BASE_URL}/create_cell"
148
+ create_data = {"public_key": pem_public.decode("utf-8")}
149
+
150
+ try:
151
+ response = requests.post(url, json=create_data, timeout=10)
152
+ response.raise_for_status()
153
+ host = response.json().get("host")
154
+
155
+ except requests.exceptions.RequestException as e:
156
+ click.echo(f"❌ Error communicating with the server: {e}")
157
+ return
158
+
159
+ # 3. Save Credentials
160
+ if host:
161
+ if save_credentials(host, mnemonic, pem_public, pem_private):
162
+ click.echo("\n" + "=" * 50)
163
+ click.echo(" ✅ WELCOME TO NEURONUM! Cell Created Successfully.")
164
+ click.echo("=" * 50)
165
+ click.echo(f" Host: {host}")
166
+ click.echo(f" Mnemonic (CRITICAL! Back this up!):")
167
+ click.echo(f" {mnemonic}")
168
+ click.echo("-" * 50)
169
+ click.echo(f"Credentials saved to: {NEURONUM_PATH}")
170
+ else:
171
+ # Error saving credentials already echoed in helper
172
+ pass
173
+ else:
174
+ click.echo("❌ Error: Server did not return a host. Cell creation failed.")
175
+
176
+
177
+ @click.command()
178
+ def connect_cell():
179
+ """Connects to an existing Community Cell using a 12-word mnemonic."""
180
+
181
+ # 1. Get and Validate Mnemonic
182
+ mnemonic = questionary.text("Enter your 12-word BIP-39 mnemonic (space separated):").ask()
183
+
184
+ if not mnemonic:
185
+ click.echo("Connection canceled.")
186
+ return
187
+
188
+ mnemonic = " ".join(mnemonic.strip().split())
189
+ words = mnemonic.split()
190
+
191
+ if len(words) != 12:
192
+ click.echo("❌ Mnemonic must be exactly 12 words.")
193
+ return
194
+
195
+ if not Bip39MnemonicValidator(Bip39Languages.ENGLISH).IsValid(mnemonic):
196
+ click.echo("❌ Invalid mnemonic. Please ensure all words are valid BIP-39 words.")
197
+ return
198
+
199
+ # 2. Derive Keys
200
+ private_key, public_key, pem_private, pem_public = derive_keys_from_mnemonic(mnemonic)
201
+ if not private_key:
202
+ return
203
+
204
+ # 3. Prepare Signed Message
205
+ timestamp = str(int(time.time()))
206
+ public_key_pem_str = pem_public.decode('utf-8')
207
+ message = f"public_key={public_key_pem_str};timestamp={timestamp}"
208
+ signature_b64 = sign_message(private_key, message.encode())
209
+
210
+ if not signature_b64:
211
+ return
212
+
213
+ # 4. Call API to Connect
214
+ click.echo("🔗 Attempting to connect to cell...")
215
+ url = f"{API_BASE_URL}/connect_cell"
216
+ connect_data = {
217
+ "public_key": public_key_pem_str,
218
+ "signed_message": signature_b64,
219
+ "message": message
220
+ }
221
+
222
+ try:
223
+ response = requests.post(url, json=connect_data, timeout=10)
224
+ response.raise_for_status()
225
+ host = response.json().get("host")
226
+ except requests.exceptions.RequestException as e:
227
+ click.echo(f"❌ Error connecting to cell: {e}")
228
+ return
229
+
230
+ # 5. Save Credentials
231
+ if host:
232
+ if save_credentials(host, mnemonic, pem_public, pem_private):
233
+ click.echo(f"🔗 Successfully connected to Community Cell '{host}'.")
234
+ # Error saving credentials already echoed in helper
235
+ else:
236
+ click.echo("❌ Failed to retrieve host from server. Connection failed.")
237
+
238
+
239
+ @click.command()
240
+ def view_cell():
241
+ """Displays the connection status and host name of the current cell."""
242
+
243
+ credentials = load_credentials()
244
+
245
+ if credentials:
246
+ click.echo("\n--- Neuronum Cell Status ---")
247
+ click.echo(f"Status: ✅ Connected")
248
+ click.echo(f"Host: {credentials['host']}")
249
+ click.echo(f"Path: {NEURONUM_PATH}")
250
+ click.echo(f"Key Type: {credentials['private_key'].curve.name} (SECP256R1)")
251
+ click.echo("----------------------------")
252
+
253
+
254
+ @click.command()
255
+ def delete_cell():
256
+ """Deletes the locally stored credentials and requests cell deletion from the server."""
257
+
258
+ # 1. Load Credentials
259
+ credentials = load_credentials()
260
+ if not credentials:
261
+ # Error already echoed in helper
262
+ return
263
+
264
+ host = credentials['host']
265
+ private_key = credentials['private_key']
266
+
267
+ # 2. Confirmation
268
+ confirm = click.confirm(f"Are you sure you want to permanently delete connection to '{host}'?", default=False)
269
+ if not confirm:
270
+ click.echo("Deletion canceled.")
271
+ return
272
+
273
+ # 3. Prepare Signed Message
274
+ timestamp = str(int(time.time()))
275
+ message = f"host={host};timestamp={timestamp}"
276
+ signature_b64 = sign_message(private_key, message.encode())
277
+
278
+ if not signature_b64:
279
+ return
280
+
281
+ # 4. Call API to Delete
282
+ click.echo(f"🗑️ Requesting deletion of cell '{host}'...")
283
+ url = f"{API_BASE_URL}/delete_cell"
284
+ payload = {
285
+ "host": host,
286
+ "signed_message": signature_b64,
287
+ "message": message
288
+ }
289
+
290
+ try:
291
+ response = requests.delete(url, json=payload, timeout=10)
292
+ response.raise_for_status()
293
+ status = response.json().get("status", False)
294
+ except requests.exceptions.RequestException as e:
295
+ click.echo(f"❌ Error communicating with the server during deletion: {e}")
296
+ return
297
+
298
+ # 5. Cleanup Local Files
299
+ if status:
300
+ try:
301
+ ENV_FILE.unlink(missing_ok=True)
302
+ PRIVATE_KEY_FILE.unlink(missing_ok=True)
303
+ PUBLIC_KEY_FILE.unlink(missing_ok=True)
304
+
305
+ click.echo(f"✅ Neuronum Cell '{host}' has been deleted and local credentials removed.")
306
+ except Exception as e:
307
+ click.echo(f"⚠️ Warning: Successfully deleted cell on server, but failed to clean up all local files: {e}")
308
+ else:
309
+ click.echo(f"❌ Neuronum Cell '{host}' deletion failed on server.")
310
+
311
+
312
+ # --- CLI Registration ---
313
+ cli.add_command(create_cell)
314
+ cli.add_command(view_cell)
315
+ cli.add_command(connect_cell)
316
+ cli.add_command(delete_cell)
317
+
318
+ if __name__ == "__main__":
319
+ cli()
@@ -0,0 +1 @@
1
+ from neuronum import Cell