distributed-state-network 0.1.3__tar.gz → 0.2.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.
Files changed (35) hide show
  1. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/PKG-INFO +5 -2
  2. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/README.md +4 -1
  3. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/documentation/ds-node-config.md +14 -8
  4. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/documentation/ds-node-server.md +2 -2
  5. distributed_state_network-0.2.0/documentation/protocol.md +65 -0
  6. distributed_state_network-0.2.0/documentation/usage.md +159 -0
  7. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/pyproject.toml +1 -1
  8. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/dsnode.py +149 -61
  9. distributed_state_network-0.2.0/src/distributed_state_network/handler.py +143 -0
  10. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/config.py +0 -4
  11. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/hello_packet.py +10 -6
  12. distributed_state_network-0.2.0/src/distributed_state_network/objects/msg_types.py +4 -0
  13. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/technical.md +6 -9
  14. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/test.py +60 -56
  15. distributed_state_network-0.1.3/documentation/protocol.md +0 -37
  16. distributed_state_network-0.1.3/documentation/usage.md +0 -84
  17. distributed_state_network-0.1.3/src/distributed_state_network/handler.py +0 -117
  18. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/.github/workflows/publish.yml +0 -0
  19. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/.gitignore +0 -0
  20. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/LICENSE +0 -0
  21. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/coverage.sh +0 -0
  22. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/documentation/ds-node.md +0 -0
  23. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/__init__.py +0 -0
  24. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/create_key.py +0 -0
  25. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/endpoint.py +0 -0
  26. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/peers_packet.py +0 -0
  27. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/signed_packet.py +0 -0
  28. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/objects/state_packet.py +0 -0
  29. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/__init__.py +0 -0
  30. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/aes.py +0 -0
  31. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/byte_helper.py +0 -0
  32. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/ecdsa.py +0 -0
  33. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/https.py +0 -0
  34. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/src/distributed_state_network/util/key_manager.py +0 -0
  35. {distributed_state_network-0.1.3 → distributed_state_network-0.2.0}/tests/connections.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: distributed-state-network
3
- Version: 0.1.3
3
+ Version: 0.2.0
4
4
  Summary: A tool to distribute the state of a network device to other devices on the network
5
5
  Project-URL: Homepage, https://github.com/erinclemmer/distributed_state_network
6
6
  Project-URL: Issues, https://github.com/erinclemmer/distributed_state_network/issues
@@ -77,7 +77,7 @@ DSN creates a peer-to-peer network where each node maintains its own state datab
77
77
  - Each node owns its state and is the only one who can modify it
78
78
  - State changes are automatically broadcast to all connected nodes
79
79
  - Any node can read any other node's state instantly
80
- - All communication is encrypted with AES + ECDSA + HTTPS
80
+ - All communication is encrypted with AES
81
81
 
82
82
  ## Example: Distributed Temperature Monitoring
83
83
 
@@ -110,6 +110,9 @@ for node_id in monitor.node.peers():
110
110
 
111
111
  ### Documentation
112
112
  * [Usage Examples](./documentation/usage.md)
113
+ * [Configuration Class](./documentation/ds-node-config.md)
114
+ * [Server Class](./documentation/ds-node-server.md)
115
+ * [Protocol Class](./documentation/ds-node.md)
113
116
  * [Protocol](./documentation/protocol.md)
114
117
 
115
118
  #### API Reference
@@ -52,7 +52,7 @@ DSN creates a peer-to-peer network where each node maintains its own state datab
52
52
  - Each node owns its state and is the only one who can modify it
53
53
  - State changes are automatically broadcast to all connected nodes
54
54
  - Any node can read any other node's state instantly
55
- - All communication is encrypted with AES + ECDSA + HTTPS
55
+ - All communication is encrypted with AES
56
56
 
57
57
  ## Example: Distributed Temperature Monitoring
58
58
 
@@ -85,6 +85,9 @@ for node_id in monitor.node.peers():
85
85
 
86
86
  ### Documentation
87
87
  * [Usage Examples](./documentation/usage.md)
88
+ * [Configuration Class](./documentation/ds-node-config.md)
89
+ * [Server Class](./documentation/ds-node-server.md)
90
+ * [Protocol Class](./documentation/ds-node.md)
88
91
  * [Protocol](./documentation/protocol.md)
89
92
 
90
93
  #### API Reference
@@ -12,20 +12,18 @@ from distributed_state_network import DSNodeConfig
12
12
  class DSNodeConfig:
13
13
  node_id: str
14
14
  port: int
15
- https: bool
16
- network_ip: str
17
15
  aes_key_file: str
18
16
  bootstrap_nodes: List[Endpoint]
19
17
  ```
20
18
 
21
19
  ### Attributes
22
20
  - **node_id** (`str`): Unique identifier for the node
23
- - **port** (`int`): Port number for the node to listen on
21
+ - **port** (`int`): Port number for the node to listen on (UDP)
24
22
  - **aes_key_file** (`str`): Path to the AES key file for encryption/decryption
25
- - **https** (`bool`): whether to use HTTPS network requests (true) or http (false)
26
- - **network_ip** (`str`): Must match network address of the current computer (only if https is on)
27
23
  - **bootstrap_nodes** (`List[Endpoint]`): List of initial nodes to connect to when joining the network
28
24
 
25
+ **Note:** The node's public IP address is automatically detected by the bootstrap server during the initial handshake.
26
+
29
27
  ### Methods
30
28
 
31
29
  ### `from_dict(data: Dict) -> DSNodeConfig`
@@ -43,11 +41,19 @@ config_dict = {
43
41
  "node_id": "node1",
44
42
  "port": 8000,
45
43
  "aes_key_file": "/path/to/key.aes",
46
- "https": false,
47
- "network_ip": "192.168.0.1",
48
44
  "bootstrap_nodes": [
49
45
  {"address": "127.0.0.1", "port": 8001}
50
46
  ]
51
47
  }
52
48
  config = DSNodeConfig.from_dict(config_dict)
53
- ```
49
+ ```
50
+
51
+ **Example for bootstrap node (first node in network):**
52
+ ```python
53
+ config_dict = {
54
+ "node_id": "bootstrap",
55
+ "port": 8000,
56
+ "aes_key_file": "/path/to/key.aes",
57
+ "bootstrap_nodes": [] # Empty for first node
58
+ }
59
+ config = DSNodeConfig.from_dict(config_dict)
@@ -30,8 +30,8 @@ server = DSNodeServer.start(config)
30
30
 
31
31
  **Parameters:**
32
32
  - `config` (`DSNodeConfig`): Node configuration
33
- - `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events
34
- - `update_callback` (`Optional[Callable]`): Callback for state update events
33
+ - `disconnect_callback` (`Optional[Callable]`): Callback for disconnect events (no parameters)
34
+ - `update_callback` (`Optional[Callable]`): Callback for state update events (no parameters)
35
35
 
36
36
  **Returns:**
37
37
  - `DSNodeServer`: Running server instance
@@ -0,0 +1,65 @@
1
+ ## Network Protocol
2
+
3
+ ### Transport Layer
4
+ The network now uses **UDP (User Datagram Protocol)** for all communication instead of HTTP/HTTPS. This provides:
5
+ - Lower latency for peer-to-peer communication
6
+ - Reduced overhead compared to HTTP
7
+ - Simpler connection management
8
+ - More suitable for real-time distributed systems
9
+
10
+ ### Message Types
11
+ All UDP packets use a message type byte prefix to identify the packet type:
12
+
13
+ - **Type 1 (HELLO)**: Exchange node information and credentials
14
+ - **Type 2 (PEERS)**: Request/share peer list
15
+ - **Type 3 (UPDATE)**: Send/receive state updates
16
+ - **Type 4 (PING)**: Connectivity check
17
+
18
+ ### Packet Structure
19
+ Each UDP packet follows this structure:
20
+ 1. **AES Encrypted Payload** containing:
21
+ - Message type (1 byte)
22
+ - Message payload (variable length)
23
+
24
+ ### Security
25
+ - All communication is encrypted using AES with a shared key
26
+ - Messages are signed using ECDSA for authentication
27
+ - UDP packets are encrypted end-to-end
28
+ - HTTPS/TLS is not supported with UDP (certificates are no longer used)
29
+
30
+ ### State Synchronization
31
+ - Nodes maintain a copy of all peers' states
32
+ - Updates are broadcast to all connected peers
33
+ - Timestamps prevent older updates from overwriting newer ones
34
+
35
+ ### Socket Management
36
+ - You can provide a custom UDP socket when starting a server
37
+ - If no socket is provided, one will be created and bound automatically
38
+ - Socket timeout is set to 2 seconds for requests
39
+ - Maximum UDP packet size is 65507 bytes
40
+
41
+ ## Important Notes
42
+
43
+ 1. **Shared AES Key**: All nodes in the network must use the same AES key file
44
+ 2. **Unique Node IDs**: Each node must have a unique node_id
45
+ 3. **Port Availability**: Ensure the specified UDP port is available before starting
46
+ 4. **Bootstrap Nodes**: At least one bootstrap node is required to join an existing network
47
+ 5. **Network Tick**: The network performs maintenance checks every 3 seconds
48
+ 6. **Credential Management**: ECDSA keys are automatically generated and stored in `credentials/` directory
49
+ 7. **UDP Reliability**: The protocol implements retry logic (up to 3 attempts) for failed requests
50
+ 8. **HTTPS Not Supported**: The `https` configuration option is ignored when using UDP
51
+
52
+ ## Error Handling
53
+
54
+ Common error codes (embedded in exception messages):
55
+ - **401**: Not Authorized (signature verification failed)
56
+ - **406**: Not Acceptable (invalid data, stale update, or version mismatch)
57
+ - **505**: Version not supported
58
+
59
+ ## Migration from HTTP
60
+
61
+ If you're migrating from the HTTP-based version:
62
+ 1. Remove any HTTPS certificate dependencies
63
+ 2. Update firewall rules to allow UDP traffic on your ports
64
+ 3. The API remains largely the same, but transport is now UDP
65
+ 4. The `https` flag in configuration is now ignored
@@ -0,0 +1,159 @@
1
+ ## Usage Examples
2
+
3
+ ### Basic Setup
4
+
5
+ ```python
6
+ from distributed_state_network import DSNodeServer, DSNodeConfig, Endpoint
7
+
8
+ # Create configuration
9
+ config = DSNodeConfig(
10
+ node_id="node1",
11
+ port=8000,
12
+ aes_key_file="/path/to/shared.key",
13
+ bootstrap_nodes=[] # Empty for first node
14
+ )
15
+
16
+ # Start server (UDP socket will be created automatically)
17
+ server = DSNodeServer.start(config)
18
+
19
+ # Update state
20
+ server.node.update_data("status", "online")
21
+ server.node.update_data("version", "1.0.0")
22
+
23
+ # Read own state
24
+ my_status = server.node.read_data("node1", "status")
25
+
26
+ # Get connected peers
27
+ peers = server.node.peers()
28
+ print(f"Connected peers: {peers}")
29
+
30
+ # Shutdown
31
+ server.stop()
32
+ ```
33
+
34
+ ### Joining Existing Network
35
+
36
+ ```python
37
+ # Node 2 configuration with bootstrap
38
+ config2 = DSNodeConfig(
39
+ node_id="node2",
40
+ port=8001,
41
+ aes_key_file="/path/to/shared.key", # Same key file as network
42
+ bootstrap_nodes=[
43
+ Endpoint("127.0.0.1", 8000) # Node 1's endpoint
44
+ ]
45
+ )
46
+
47
+ server2 = DSNodeServer.start(config2)
48
+
49
+ # Read state from peer
50
+ peer_status = server2.node.read_data("node1", "status")
51
+ ```
52
+
53
+ ### With Custom Socket
54
+
55
+ ```python
56
+ import socket
57
+
58
+ # Create a custom UDP socket
59
+ custom_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60
+ custom_socket.bind(("0.0.0.0", 8003))
61
+
62
+ # You can configure socket options before passing it to the server
63
+ custom_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
64
+
65
+ config = DSNodeConfig(
66
+ node_id="node3",
67
+ port=8003, # Should match the bound port
68
+ aes_key_file="/path/to/shared.key",
69
+ bootstrap_nodes=[Endpoint("127.0.0.1", 8000)]
70
+ )
71
+
72
+ # Pass the custom socket when starting the server
73
+ server = DSNodeServer.start(config, sock=custom_socket)
74
+ ```
75
+
76
+ ### With Disconnect Callback
77
+
78
+ ```python
79
+ def handle_disconnect():
80
+ print("A peer has disconnected!")
81
+ # Handle reconnection logic
82
+
83
+ config = DSNodeConfig(
84
+ node_id="node4",
85
+ port=8004,
86
+ aes_key_file="/path/to/shared.key",
87
+ bootstrap_nodes=[Endpoint("127.0.0.1", 8000)]
88
+ )
89
+
90
+ server = DSNodeServer.start(config, disconnect_callback=handle_disconnect)
91
+ ```
92
+
93
+ ### With Update Callback
94
+
95
+ ```python
96
+ def handle_update():
97
+ print("A peer has updated their state!")
98
+ # React to state changes
99
+
100
+ config = DSNodeConfig(
101
+ node_id="node5",
102
+ port=8005,
103
+ aes_key_file="/path/to/shared.key",
104
+ bootstrap_nodes=[Endpoint("127.0.0.1", 8000)]
105
+ )
106
+
107
+ server = DSNodeServer.start(config, update_callback=handle_update)
108
+ ```
109
+
110
+ ### Complete Example with All Options
111
+
112
+ ```python
113
+ import socket
114
+ from distributed_state_network import DSNodeServer, DSNodeConfig, Endpoint
115
+
116
+ def on_peer_disconnect():
117
+ print("Peer disconnected from network")
118
+
119
+ def on_state_update():
120
+ print("Peer state was updated")
121
+
122
+ # Create custom socket (optional)
123
+ custom_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
124
+ custom_sock.bind(("0.0.0.0", 8006))
125
+
126
+ config = DSNodeConfig(
127
+ node_id="node6",
128
+ port=8006,
129
+ aes_key_file="/path/to/shared.key",
130
+ bootstrap_nodes=[
131
+ Endpoint("127.0.0.1", 8000),
132
+ Endpoint("127.0.0.1", 8001) # Multiple bootstrap options
133
+ ]
134
+ )
135
+
136
+ # Start with all options
137
+ server = DSNodeServer.start(
138
+ config,
139
+ sock=custom_sock, # Optional custom socket
140
+ disconnect_callback=on_peer_disconnect,
141
+ update_callback=on_state_update
142
+ )
143
+
144
+ # Use the server
145
+ server.node.update_data("role", "worker")
146
+ peers = server.node.peers()
147
+
148
+ # When done
149
+ server.stop()
150
+ ```
151
+
152
+ ### Network Protocol Notes
153
+
154
+ - All communication uses **UDP** instead of HTTP/HTTPS
155
+ - Packets are encrypted with AES using the shared key
156
+ - Messages are signed with ECDSA for authentication
157
+ - Maximum UDP packet size is 65507 bytes
158
+ - Request timeout is 2 seconds with up to 3 retry attempts
159
+ - Network health checks occur every 3 seconds
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "distributed-state-network"
7
- version = "0.1.3"
7
+ version = "0.2.0"
8
8
  authors = [{name="Erin Clemmer", email="erin.c.clemmer@gmail.com"}]
9
9
  description = "A tool to distribute the state of a network device to other devices on the network"
10
10
  keywords = ["distributed", "networking"]