redundanet 2.0.0__py3-none-any.whl → 2.0.2__py3-none-any.whl

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.
redundanet/__init__.py CHANGED
@@ -8,9 +8,9 @@ from redundanet.core.config import NetworkConfig, NodeConfig, TahoeConfig
8
8
  from redundanet.core.manifest import Manifest
9
9
 
10
10
  __all__ = [
11
- "__version__",
11
+ "Manifest",
12
12
  "NetworkConfig",
13
13
  "NodeConfig",
14
14
  "TahoeConfig",
15
- "Manifest",
15
+ "__version__",
16
16
  ]
redundanet/auth/gpg.py CHANGED
@@ -43,9 +43,9 @@ class GPGKeyInfo:
43
43
  fingerprint=key.get("fingerprint", ""),
44
44
  user_id=user_id,
45
45
  email=email,
46
- created=key.get("date", None),
47
- expires=key.get("expires", None),
48
- trust=key.get("trust", None),
46
+ created=key.get("date"),
47
+ expires=key.get("expires"),
48
+ trust=key.get("trust"),
49
49
  )
50
50
 
51
51
 
@@ -107,7 +107,7 @@ class GPGManager:
107
107
 
108
108
  key = self._gpg.gen_key(input_data)
109
109
 
110
- if not key.ok:
110
+ if not key.fingerprint:
111
111
  raise GPGError(f"Failed to generate GPG key: {key.status}")
112
112
 
113
113
  logger.info("GPG key generated", fingerprint=str(key))
redundanet/cli/node.py CHANGED
@@ -266,40 +266,223 @@ def remove_node(
266
266
  def manage_keys(
267
267
  action: Annotated[
268
268
  str,
269
- typer.Argument(help="Action: generate, export, import"),
269
+ typer.Argument(help="Action: generate, export, import, list, publish, fetch"),
270
270
  ],
271
271
  node_name: Annotated[
272
272
  Optional[str],
273
273
  typer.Option("--name", "-n", help="Node name"),
274
274
  ] = None,
275
+ email: Annotated[
276
+ Optional[str],
277
+ typer.Option("--email", "-e", help="Email address for the GPG key"),
278
+ ] = None,
279
+ key_id: Annotated[
280
+ Optional[str],
281
+ typer.Option("--key-id", "-k", help="Key ID for export/import operations"),
282
+ ] = None,
283
+ output_file: Annotated[
284
+ Optional[Path],
285
+ typer.Option("--output", "-o", help="Output file for export"),
286
+ ] = None,
287
+ input_file: Annotated[
288
+ Optional[Path],
289
+ typer.Option("--input", "-i", help="Input file for import"),
290
+ ] = None,
275
291
  ) -> None:
276
292
  """Manage GPG keys for node authentication."""
293
+ from redundanet.auth.gpg import GPGManager
277
294
  from redundanet.core.config import load_settings
295
+ from redundanet.core.exceptions import GPGError
278
296
 
279
297
  settings = load_settings()
280
298
  node_name = node_name or settings.node_name
281
299
 
282
- if not node_name:
283
- console.print("[red]Error:[/red] No node name specified")
284
- console.print("Use --name or set REDUNDANET_NODE_NAME")
285
- raise typer.Exit(1)
286
-
287
300
  if action == "generate":
301
+ if not node_name:
302
+ console.print("[red]Error:[/red] No node name specified")
303
+ console.print("Use --name or set REDUNDANET_NODE_NAME")
304
+ raise typer.Exit(1)
305
+
306
+ # Prompt for email if not provided
307
+ if not email:
308
+ email = typer.prompt("Enter email address for the GPG key")
309
+
288
310
  console.print(f"[bold]Generating GPG key for node: {node_name}[/bold]")
289
311
  console.print("[yellow]Note:[/yellow] This will create a new GPG keypair.")
290
- console.print("Use 'redundanet node keys export' to share your public key.")
291
- # In a real implementation, we'd call the GPG module
292
- console.print("[dim]GPG key generation not yet implemented[/dim]")
312
+
313
+ try:
314
+ gpg = GPGManager(node_name=node_name)
315
+ with console.status("[bold green]Generating GPG key (this may take a moment)..."):
316
+ key_info = gpg.generate_key(
317
+ name=f"RedundaNet Node {node_name}",
318
+ email=email,
319
+ )
320
+
321
+ console.print("\n[bold green]GPG key generated successfully![/bold green]")
322
+ console.print(f" Key ID: [cyan]{key_info.key_id}[/cyan]")
323
+ console.print(f" Fingerprint: [dim]{key_info.fingerprint}[/dim]")
324
+ console.print(f" User ID: {key_info.user_id}")
325
+
326
+ console.print("\n[bold]Next steps:[/bold]")
327
+ console.print(
328
+ f"1. Publish your key to keyservers: [cyan]redundanet node keys publish --key-id {key_info.key_id}[/cyan]"
329
+ )
330
+ console.print(
331
+ "2. Submit your application at: [cyan]https://redundanet.com/join.html[/cyan]"
332
+ )
333
+ console.print(f"3. Use this Key ID in your application: [cyan]{key_info.key_id}[/cyan]")
334
+
335
+ except GPGError as e:
336
+ console.print(f"[red]Error generating key:[/red] {e}")
337
+ raise typer.Exit(1) from None
293
338
 
294
339
  elif action == "export":
295
- console.print(f"[bold]Exporting public key for node: {node_name}[/bold]")
296
- console.print("[dim]Key export not yet implemented[/dim]")
340
+ if not key_id:
341
+ console.print("[red]Error:[/red] --key-id is required for export")
342
+ raise typer.Exit(1)
343
+
344
+ console.print(f"[bold]Exporting public key: {key_id}[/bold]")
345
+
346
+ try:
347
+ gpg = GPGManager()
348
+ public_key = gpg.export_public_key(key_id)
349
+
350
+ if output_file:
351
+ output_file.write_text(public_key)
352
+ console.print(f"[green]Public key exported to:[/green] {output_file}")
353
+ else:
354
+ console.print("\n[bold]Public Key:[/bold]")
355
+ console.print(public_key)
356
+
357
+ except GPGError as e:
358
+ console.print(f"[red]Error exporting key:[/red] {e}")
359
+ raise typer.Exit(1) from None
297
360
 
298
361
  elif action == "import":
299
- console.print("[bold]Importing public key[/bold]")
300
- console.print("[dim]Key import not yet implemented[/dim]")
362
+ if not input_file:
363
+ console.print("[red]Error:[/red] --input is required for import")
364
+ console.print("Provide a path to an ASCII-armored public key file")
365
+ raise typer.Exit(1)
366
+
367
+ if not input_file.exists():
368
+ console.print(f"[red]Error:[/red] File not found: {input_file}")
369
+ raise typer.Exit(1)
370
+
371
+ console.print(f"[bold]Importing public key from: {input_file}[/bold]")
372
+
373
+ try:
374
+ gpg = GPGManager()
375
+ key_data = input_file.read_text()
376
+ key_info = gpg.import_key(key_data)
377
+
378
+ console.print("[green]Key imported successfully![/green]")
379
+ console.print(f" Key ID: [cyan]{key_info.key_id}[/cyan]")
380
+ console.print(f" Fingerprint: [dim]{key_info.fingerprint}[/dim]")
381
+ console.print(f" User ID: {key_info.user_id}")
382
+
383
+ except GPGError as e:
384
+ console.print(f"[red]Error importing key:[/red] {e}")
385
+ raise typer.Exit(1) from None
386
+
387
+ elif action == "list":
388
+ console.print("[bold]GPG Keys in Keyring:[/bold]")
389
+
390
+ try:
391
+ gpg = GPGManager()
392
+ keys = gpg.list_keys()
393
+
394
+ if not keys:
395
+ console.print("[dim]No keys found in keyring[/dim]")
396
+ return
397
+
398
+ table = Table()
399
+ table.add_column("Key ID", style="cyan")
400
+ table.add_column("User ID")
401
+ table.add_column("Created")
402
+ table.add_column("Expires")
403
+
404
+ for key in keys:
405
+ table.add_row(
406
+ key.key_id,
407
+ key.user_id,
408
+ key.created or "[dim]unknown[/dim]",
409
+ key.expires or "[dim]never[/dim]",
410
+ )
411
+
412
+ console.print(table)
413
+
414
+ except GPGError as e:
415
+ console.print(f"[red]Error listing keys:[/red] {e}")
416
+ raise typer.Exit(1) from None
417
+
418
+ elif action == "publish":
419
+ if not key_id:
420
+ console.print("[red]Error:[/red] --key-id is required for publish")
421
+ console.print("Use 'redundanet node keys list' to see your keys")
422
+ raise typer.Exit(1)
423
+
424
+ console.print(f"[bold]Publishing key to keyservers: {key_id}[/bold]")
425
+
426
+ try:
427
+ from redundanet.auth.keyserver import KeyServerClient
428
+ from redundanet.core.exceptions import KeyServerError
429
+
430
+ gpg = GPGManager()
431
+ keyserver_client = KeyServerClient(gpg)
432
+
433
+ with console.status("[bold green]Uploading to keyservers..."):
434
+ success = keyserver_client.upload_key(key_id)
435
+
436
+ if success:
437
+ console.print("[green]Key published successfully![/green]")
438
+ console.print("\nYour key is now available on public keyservers.")
439
+ console.print("Note: It may take a few minutes for the key to propagate.")
440
+ console.print("\n[bold]Keyservers used:[/bold]")
441
+ for server in keyserver_client.keyservers:
442
+ console.print(f" - {server}")
443
+ else:
444
+ console.print("[yellow]Warning:[/yellow] Failed to upload to any keyserver")
445
+ console.print("You may need to manually upload your key at:")
446
+ console.print(" - https://keys.openpgp.org/upload")
447
+ console.print(" - https://keyserver.ubuntu.com/")
448
+
449
+ except (GPGError, KeyServerError) as e:
450
+ console.print(f"[red]Error publishing key:[/red] {e}")
451
+ raise typer.Exit(1) from None
452
+
453
+ elif action == "fetch":
454
+ if not key_id:
455
+ console.print("[red]Error:[/red] --key-id is required for fetch")
456
+ raise typer.Exit(1)
457
+
458
+ console.print(f"[bold]Fetching key from keyservers: {key_id}[/bold]")
459
+
460
+ try:
461
+ from redundanet.auth.keyserver import KeyServerClient
462
+
463
+ gpg = GPGManager()
464
+ keyserver_client = KeyServerClient(gpg)
465
+
466
+ with console.status("[bold green]Searching keyservers..."):
467
+ success = keyserver_client.import_key_from_server(key_id)
468
+
469
+ if success:
470
+ console.print("[green]Key fetched and imported successfully![/green]")
471
+
472
+ # Show key info
473
+ fetched_key = gpg.get_key(key_id)
474
+ if fetched_key:
475
+ console.print(f" Key ID: [cyan]{fetched_key.key_id}[/cyan]")
476
+ console.print(f" User ID: {fetched_key.user_id}")
477
+ else:
478
+ console.print(f"[red]Error:[/red] Key {key_id} not found on any keyserver")
479
+ raise typer.Exit(1)
480
+
481
+ except GPGError as e:
482
+ console.print(f"[red]Error fetching key:[/red] {e}")
483
+ raise typer.Exit(1) from None
301
484
 
302
485
  else:
303
486
  console.print(f"[red]Error:[/red] Unknown action '{action}'")
304
- console.print("Valid actions: generate, export, import")
487
+ console.print("Valid actions: generate, export, import, list, publish, fetch")
305
488
  raise typer.Exit(1)
@@ -5,7 +5,7 @@ from redundanet.network.dns import DNSManager
5
5
  from redundanet.network.validation import NetworkValidator
6
6
 
7
7
  __all__ = [
8
- "NodeDiscovery",
9
- "NetworkValidator",
10
8
  "DNSManager",
9
+ "NetworkValidator",
10
+ "NodeDiscovery",
11
11
  ]
@@ -6,8 +6,8 @@ from redundanet.storage.introducer import TahoeIntroducer
6
6
  from redundanet.storage.storage import TahoeStorage
7
7
 
8
8
  __all__ = [
9
+ "FURLManager",
9
10
  "TahoeClient",
10
- "TahoeStorage",
11
11
  "TahoeIntroducer",
12
- "FURLManager",
12
+ "TahoeStorage",
13
13
  ]
@@ -171,7 +171,7 @@ async def run_command_async(
171
171
  command=cmd_str,
172
172
  )
173
173
 
174
- except asyncio.TimeoutError:
174
+ except TimeoutError:
175
175
  process.kill()
176
176
  await process.wait()
177
177
  logger.error("Async command timed out", command=cmd_str, timeout=timeout)
@@ -5,8 +5,8 @@ from redundanet.vpn.mesh import MeshNetwork
5
5
  from redundanet.vpn.tinc import TincConfig, TincManager
6
6
 
7
7
  __all__ = [
8
- "TincManager",
8
+ "MeshNetwork",
9
9
  "TincConfig",
10
+ "TincManager",
10
11
  "VPNKeyManager",
11
- "MeshNetwork",
12
12
  ]
@@ -0,0 +1,298 @@
1
+ Metadata-Version: 2.1
2
+ Name: redundanet
3
+ Version: 2.0.2
4
+ Summary: Distributed encrypted storage on a mesh VPN network
5
+ Home-page: https://github.com/alessandrodefilippo/redundanet
6
+ License: MIT
7
+ Keywords: distributed-storage,vpn,encryption,mesh-network,tahoe-lafs
8
+ Author: Alessandro De Filippo
9
+ Author-email: alessandro@example.com
10
+ Requires-Python: >=3.11,<4.0
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Security :: Cryptography
21
+ Classifier: Topic :: System :: Distributed Computing
22
+ Classifier: Topic :: System :: Networking
23
+ Requires-Dist: gitpython (>=3.1,<4.0)
24
+ Requires-Dist: httpx (>=0.26,<0.27)
25
+ Requires-Dist: jinja2 (>=3.1,<4.0)
26
+ Requires-Dist: jsonschema (>=4.21,<5.0)
27
+ Requires-Dist: psutil (>=5.9,<6.0)
28
+ Requires-Dist: pydantic (>=2.5,<3.0)
29
+ Requires-Dist: pydantic-settings (>=2.1,<3.0)
30
+ Requires-Dist: python-gnupg (>=0.5,<0.6)
31
+ Requires-Dist: pyyaml (>=6.0,<7.0)
32
+ Requires-Dist: rich (>=13.7,<14.0)
33
+ Requires-Dist: structlog (>=24.1,<25.0)
34
+ Requires-Dist: typer[all] (>=0.12.0)
35
+ Requires-Dist: watchdog (>=4.0,<5.0)
36
+ Project-URL: Documentation, https://redundanet.readthedocs.io
37
+ Project-URL: Repository, https://github.com/alessandrodefilippo/redundanet
38
+ Description-Content-Type: text/markdown
39
+
40
+ # RedundaNet - Distributed Encrypted Storage Network
41
+
42
+ [![CI](https://github.com/adefilippo83/redundanet/actions/workflows/ci.yml/badge.svg)](https://github.com/adefilippo83/redundanet/actions/workflows/ci.yml)
43
+ [![PyPI](https://img.shields.io/pypi/v/redundanet)](https://pypi.org/project/redundanet/)
44
+ [![License](https://img.shields.io/github/license/adefilippo83/redundanet)](LICENSE)
45
+
46
+ RedundaNet is a distributed, encrypted storage system built on a secure mesh VPN network. It enables users to contribute storage resources to a collective grid while maintaining privacy through end-to-end encryption.
47
+
48
+ **Website**: [https://redundanet.com](https://redundanet.com)
49
+
50
+ ## Features
51
+
52
+ - **Decentralized Architecture**: No central authority or single point of failure
53
+ - **End-to-End Encryption**: Data is encrypted before leaving your device using Tahoe-LAFS
54
+ - **GPG-Based Authentication**: Secure node identity verification via public keyservers
55
+ - **Private Networking**: Secure Tinc mesh VPN isolates the storage network
56
+ - **Erasure Coding**: Data is split and distributed across multiple nodes (3-of-10 scheme)
57
+ - **Open Membership**: Anyone can apply to join the network
58
+ - **Containerized Deployment**: Easy setup with Docker Compose
59
+ - **Raspberry Pi Ready**: Pre-built images for ARM devices
60
+
61
+ ## How It Works
62
+
63
+ ```
64
+ ┌─────────────────────────────────────────────────────────────────────────┐
65
+ │ RedundaNet Network │
66
+ │ │
67
+ │ Your File │
68
+ │ │ │
69
+ │ ▼ │
70
+ │ ┌─────────┐ Encrypted ┌─────────┐ │
71
+ │ │ Encrypt │ ──────────────────►│ Split │ │
72
+ │ │ (AES) │ │(Erasure)│ │
73
+ │ └─────────┘ └────┬────┘ │
74
+ │ │ │
75
+ │ ┌────────────────────────┼────────────────────────┐ │
76
+ │ ▼ ▼ ▼ ▼ ▼ │
77
+ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
78
+ │ │Share 1│ │Share 2│ │Share 3│ │ ... │ │Share10│ │
79
+ │ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ └───┬───┘ │
80
+ │ │ │ │ │ │ │
81
+ │ ▼ ▼ ▼ ▼ ▼ │
82
+ │ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
83
+ │ │Node A │ │Node B │ │Node C │ │Node D │ │Node E │ │
84
+ │ │ (VPN) │◄──►│ (VPN) │◄─►│ (VPN) │◄─►│ (VPN) │◄──►│ (VPN) │ │
85
+ │ └───────┘ └───────┘ └───────┘ └───────┘ └───────┘ │
86
+ │ │
87
+ │ Only 3 of 10 shares needed to reconstruct your file │
88
+ └─────────────────────────────────────────────────────────────────────────┘
89
+ ```
90
+
91
+ **Key Concepts:**
92
+ - **Your data is encrypted** on your device before upload - nodes cannot read your files
93
+ - **Erasure coding** splits data across nodes - any 3 of 10 nodes can reconstruct your file
94
+ - **Mesh VPN** connects all nodes securely - no central server required
95
+ - **GPG keys** verify node identity - published to public keyservers
96
+
97
+ ## Join the Network
98
+
99
+ Want to contribute storage and join RedundaNet? Here's how:
100
+
101
+ ### 1. Generate and Publish Your GPG Key
102
+
103
+ ```bash
104
+ # Install the CLI
105
+ pip install redundanet
106
+
107
+ # Generate a GPG key for your node
108
+ redundanet node keys generate --name my-node --email you@example.com
109
+
110
+ # Publish your key to public keyservers
111
+ redundanet node keys publish --key-id YOUR_KEY_ID
112
+ ```
113
+
114
+ ### 2. Submit Your Application
115
+
116
+ Visit [redundanet.com/join.html](https://redundanet.com/join.html) and fill out the application form with:
117
+ - Your GPG Key ID
118
+ - Storage contribution (how much space you'll share)
119
+ - Your region
120
+ - Device type
121
+
122
+ This creates a GitHub issue that's automatically processed.
123
+
124
+ ### 3. Wait for Approval
125
+
126
+ A maintainer will review your application and merge the PR that adds your node to the network manifest.
127
+
128
+ ### 4. Set Up Your Node
129
+
130
+ Once approved:
131
+
132
+ ```bash
133
+ # Clone the repository
134
+ git clone https://github.com/adefilippo83/redundanet.git
135
+ cd redundanet
136
+
137
+ # Initialize your node (use the name assigned to you)
138
+ redundanet init --name node-XXXXXXXX
139
+
140
+ # Sync the manifest
141
+ redundanet sync
142
+
143
+ # Start services
144
+ docker compose up -d
145
+
146
+ # Check status
147
+ redundanet status
148
+ ```
149
+
150
+ ## Quick Start (Existing Network Members)
151
+
152
+ ### Prerequisites
153
+
154
+ - Python 3.11+
155
+ - Docker and Docker Compose
156
+ - GPG (for key management)
157
+
158
+ ### Installation
159
+
160
+ ```bash
161
+ pip install redundanet
162
+ ```
163
+
164
+ ### Start Services
165
+
166
+ ```bash
167
+ # As a storage node (contributes storage)
168
+ docker compose --profile storage up -d
169
+
170
+ # As a client only (uses storage)
171
+ docker compose --profile client up -d
172
+ ```
173
+
174
+ ### Upload and Download Files
175
+
176
+ ```bash
177
+ # Upload a file
178
+ redundanet storage upload /path/to/file.txt
179
+ # Returns: URI:CHK:abc123...
180
+
181
+ # Download a file
182
+ redundanet storage download URI:CHK:abc123... /path/to/output.txt
183
+ ```
184
+
185
+ ## CLI Commands
186
+
187
+ ```
188
+ redundanet --help
189
+
190
+ Commands:
191
+ init Initialize a new node
192
+ status Show node and network status
193
+ sync Sync manifest from repository
194
+ validate Validate manifest file
195
+
196
+ node Node management commands
197
+ list List all nodes in the network
198
+ info Show detailed node information
199
+ add Add a new node to manifest
200
+ remove Remove a node from manifest
201
+ keys Manage GPG keys (generate, export, import, publish, fetch, list)
202
+
203
+ network Network management
204
+ join Join an existing network
205
+ leave Leave the network
206
+ peers Show connected peers
207
+ vpn VPN management (start/stop/status)
208
+
209
+ storage Storage management
210
+ status Show storage status
211
+ mount Mount Tahoe filesystem
212
+ unmount Unmount filesystem
213
+ upload Upload a file
214
+ download Download a file
215
+ ```
216
+
217
+ ## Raspberry Pi
218
+
219
+ Pre-built images are available for Raspberry Pi:
220
+
221
+ 1. Download from [GitHub Releases](https://github.com/adefilippo83/redundanet/releases)
222
+ 2. Flash to SD card using Raspberry Pi Imager
223
+ 3. Boot and SSH in: `ssh redundanet@redundanet.local` (password: `redundanet`)
224
+ 4. Run `redundanet init` to configure
225
+
226
+ ## Architecture
227
+
228
+ ```mermaid
229
+ graph TD
230
+ subgraph "Your Device"
231
+ A[redundanet CLI] --> B[Tahoe Client]
232
+ B --> C[Tinc VPN]
233
+ end
234
+
235
+ subgraph "Network Nodes"
236
+ D[Tinc VPN] --> E[Tahoe Storage]
237
+ F[Tinc VPN] --> G[Tahoe Storage]
238
+ H[Tinc VPN] --> I[Tahoe Introducer]
239
+ end
240
+
241
+ C -->|Encrypted Mesh| D
242
+ C -->|Encrypted Mesh| F
243
+ C -->|Encrypted Mesh| H
244
+ ```
245
+
246
+ **Components:**
247
+ - **Tinc VPN**: Creates encrypted mesh network between all nodes
248
+ - **Tahoe-LAFS**: Handles encryption, erasure coding, and distributed storage
249
+ - **GPG**: Authenticates node identity via public keyservers
250
+ - **Manifest**: YAML file in Git defining network configuration
251
+
252
+ ## Documentation
253
+
254
+ - [Installation Guide](docs/installation.md)
255
+ - [Quick Start Guide](docs/quickstart.md)
256
+ - [Configuration Reference](docs/configuration.md)
257
+ - [Architecture Overview](docs/architecture.md)
258
+
259
+ ## Development
260
+
261
+ ```bash
262
+ # Clone repository
263
+ git clone https://github.com/adefilippo83/redundanet.git
264
+ cd redundanet
265
+
266
+ # Install dependencies
267
+ make install
268
+
269
+ # Run tests
270
+ make test
271
+
272
+ # Run linting
273
+ make lint
274
+
275
+ # Run type checking
276
+ make type-check
277
+ ```
278
+
279
+ ## Contributing
280
+
281
+ 1. Fork the repository
282
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
283
+ 3. Make your changes
284
+ 4. Run tests (`make test`) and linting (`make lint`)
285
+ 5. Commit your changes
286
+ 6. Push to the branch (`git push origin feature/amazing-feature`)
287
+ 7. Open a Pull Request
288
+
289
+ ## License
290
+
291
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
292
+
293
+ ## Acknowledgments
294
+
295
+ - [Tahoe-LAFS](https://tahoe-lafs.org/) - Distributed storage system
296
+ - [Tinc VPN](https://www.tinc-vpn.org/) - Mesh VPN daemon
297
+ - [Typer](https://typer.tiangolo.com/) - CLI framework
298
+
@@ -1,23 +1,23 @@
1
- redundanet/__init__.py,sha256=alb0XFwGxNY-c3R7wZtFBRluszSmqROQ-Vo7Um1F7Xc,398
1
+ redundanet/__init__.py,sha256=tqG349jbZCq9GmU1zT4gYuVUArBzPiJmyf31kzFXqUA,398
2
2
  redundanet/__main__.py,sha256=sI1D-Mn0JeICtGPuNLp-uthhIQxOB_6_9Avf1-iOXLQ,151
3
3
  redundanet/auth/__init__.py,sha256=BlmLa620gKsRdiD3d8tsDDweA9kI_fTBY9bxM6jtiyU,202
4
- redundanet/auth/gpg.py,sha256=OG6Gdo19TYzVsyfp9-a8BLxV79gX_P3GcyMe8H_rmh4,9098
4
+ redundanet/auth/gpg.py,sha256=ztklRsvXqZ1c67cprvjGK7WNdfFdow1iG2F-oHnWBT8,9089
5
5
  redundanet/auth/keyserver.py,sha256=1zNIOIY62JaAPNWVjZaBHlx2fSN601WsGsR_KizdiAo,6502
6
6
  redundanet/cli/__init__.py,sha256=4XqFI3iyLQO6ko2bcSq_OoMvlrk8voMK8twcsHgUziY,89
7
7
  redundanet/cli/main.py,sha256=teFau_SP_rb5aDIBNH7vgwcOa8wjXoLW3SoL36kLQZ0,8129
8
8
  redundanet/cli/network.py,sha256=K5d03q4t3CBJXEreAQ8WFAsWv2qBhQUIfCE1_1lQcY4,5796
9
- redundanet/cli/node.py,sha256=z9JyrBdJfpqA7s65_TnyXI_70-7WpH1GlBhG13NfZyM,10161
9
+ redundanet/cli/node.py,sha256=AD9ijvoNKlVY_uR2QQTHrcmnwuetWXj76M1jZ_IOKEE,17230
10
10
  redundanet/cli/storage.py,sha256=I-n8NO_x_v7vKp3bx3yxfYNLNHNyOOuXMpl0T4mmZzs,8054
11
11
  redundanet/core/__init__.py,sha256=fOx3CxtyI4s2wPmBzAuqkcGq5JLKKS3BvebuUandNBQ,673
12
12
  redundanet/core/config.py,sha256=ZlEaR9i1aVHtnWcTE-BHvu3vwpYxd5q0rhlwT6hKh3g,6328
13
13
  redundanet/core/exceptions.py,sha256=sCeMw0YU7FFdpZkav5PW64-PTuGUPd2_7TaGlsJh9lY,1835
14
14
  redundanet/core/manifest.py,sha256=nW0BEtKEwlh5QQPUCeR8Anya1a8CWJ5-JwBYNW18JBA,12257
15
15
  redundanet/core/node.py,sha256=gMtsDVLz1gSZjttoU7jBUQfuQrHV3yDY2ts1yvZL780,4140
16
- redundanet/network/__init__.py,sha256=8t1sNA9P7xOC_IHN_Tbo0qFakQBTmrriuh-M2fzB41Y,286
16
+ redundanet/network/__init__.py,sha256=Lm17nf722GS1uKgQXAlccnFoFbDNEkoaOP7MwfqwD_U,286
17
17
  redundanet/network/discovery.py,sha256=37bu-EL5IKnYHxuup1wBJo3498q9stER-yRX3_6TKlk,6261
18
18
  redundanet/network/dns.py,sha256=CneCQldM_QmRaJQJKx3YjygRKktfLjT6y3qJFb48tVc,5305
19
19
  redundanet/network/validation.py,sha256=DhjnyknkiCd-FYDz30GcewYC-7lMZCfPGGrBm_DpnPg,8952
20
- redundanet/storage/__init__.py,sha256=a3e4hfWqY-Gczpi-aMlXEKgQIXAO67K5xc3uBn8I1bI,353
20
+ redundanet/storage/__init__.py,sha256=QDmT9ZP-Fr-DzwUTfe1gkLCxBg86nhoS-F0yRuyYdf8,353
21
21
  redundanet/storage/client.py,sha256=FIrgpyZe2ovirvGApk73mtVycZDohuk6Dnnc0KxuCCo,9312
22
22
  redundanet/storage/furl.py,sha256=z5s_5344JN6lYRxdt35d7PFL0wtLvBaWdG3Pw4ztLbo,5560
23
23
  redundanet/storage/introducer.py,sha256=qhGI3lUOrxCYdx3anF8hFd5efSvDu_JKFPQFBcJI3yM,5483
@@ -25,13 +25,13 @@ redundanet/storage/storage.py,sha256=eedGkr4nr6wUHf4UxMOiI9iyhcq-QEba9qLwEL7kJyY
25
25
  redundanet/utils/__init__.py,sha256=gGWh6jgJZLZuRUBKXmIxbMMxsy0B6oU5p63UxUHFcO0,392
26
26
  redundanet/utils/files.py,sha256=Mo-_TevoDgVpYo_qMvSLx8CBYXWltz66AOMtIJM6sQA,3761
27
27
  redundanet/utils/logging.py,sha256=wvun1z7C2doOCDZHZGYLyW63h5V07TrfS9lH0nOeKOg,2779
28
- redundanet/utils/process.py,sha256=TZQO9bfwKYOu4OApBEFypiQLyWAenowIu-6HVREACw0,6157
29
- redundanet/vpn/__init__.py,sha256=MB3Bh4xrCCZUoj1d2BkfbbSbmwL7652lMO76Re6dEjk,283
28
+ redundanet/utils/process.py,sha256=n-M3dscymWN-Q4iijbmBo9cjF--IhAUJ-rGVK6TD-XM,6149
29
+ redundanet/vpn/__init__.py,sha256=wM2iePNDGH-CqsNgnEaEecn6Udrx6DTGnk465vso_sg,283
30
30
  redundanet/vpn/keys.py,sha256=yTfLpqguTNOZlsvgzMS4vq9WTgVL9AUVTcZbftY6a3w,5326
31
31
  redundanet/vpn/mesh.py,sha256=EVdYcQY2reV_UESXRHEUKEIhhSSwaG7u1vvPtV6PDZI,6041
32
32
  redundanet/vpn/tinc.py,sha256=u5j2ZMPOjTfXM4LPHjg7NZmYdTF51kg58qTuA7CDtTw,10288
33
- redundanet-2.0.0.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
34
- redundanet-2.0.0.dist-info/METADATA,sha256=x9hHnnwtCPaRwVWw2bGB0SHyUFs2IK6A6huPCpVH7pc,7252
35
- redundanet-2.0.0.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
36
- redundanet-2.0.0.dist-info/entry_points.txt,sha256=-0cdQEyYsTHavFdq-k_uws2bxnQrD2Ud0FfllcHGrcw,54
37
- redundanet-2.0.0.dist-info/RECORD,,
33
+ redundanet-2.0.2.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
34
+ redundanet-2.0.2.dist-info/METADATA,sha256=K4Xkx4wWl_jLIH_HYjDwtKDgF53nqztZ4SV7OxrsFcU,10939
35
+ redundanet-2.0.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
36
+ redundanet-2.0.2.dist-info/entry_points.txt,sha256=-0cdQEyYsTHavFdq-k_uws2bxnQrD2Ud0FfllcHGrcw,54
37
+ redundanet-2.0.2.dist-info/RECORD,,
@@ -1,265 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: redundanet
3
- Version: 2.0.0
4
- Summary: Distributed encrypted storage on a mesh VPN network
5
- Home-page: https://github.com/alessandrodefilippo/redundanet
6
- License: MIT
7
- Keywords: distributed-storage,vpn,encryption,mesh-network,tahoe-lafs
8
- Author: Alessandro De Filippo
9
- Author-email: alessandro@example.com
10
- Requires-Python: >=3.11,<4.0
11
- Classifier: Development Status :: 4 - Beta
12
- Classifier: Environment :: Console
13
- Classifier: Intended Audience :: Developers
14
- Classifier: Intended Audience :: System Administrators
15
- Classifier: License :: OSI Approved :: MIT License
16
- Classifier: Operating System :: POSIX :: Linux
17
- Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Topic :: Security :: Cryptography
21
- Classifier: Topic :: System :: Distributed Computing
22
- Classifier: Topic :: System :: Networking
23
- Requires-Dist: gitpython (>=3.1,<4.0)
24
- Requires-Dist: httpx (>=0.26,<0.27)
25
- Requires-Dist: jinja2 (>=3.1,<4.0)
26
- Requires-Dist: jsonschema (>=4.21,<5.0)
27
- Requires-Dist: psutil (>=5.9,<6.0)
28
- Requires-Dist: pydantic (>=2.5,<3.0)
29
- Requires-Dist: pydantic-settings (>=2.1,<3.0)
30
- Requires-Dist: python-gnupg (>=0.5,<0.6)
31
- Requires-Dist: pyyaml (>=6.0,<7.0)
32
- Requires-Dist: rich (>=13.7,<14.0)
33
- Requires-Dist: structlog (>=24.1,<25.0)
34
- Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
35
- Requires-Dist: watchdog (>=4.0,<5.0)
36
- Project-URL: Documentation, https://redundanet.readthedocs.io
37
- Project-URL: Repository, https://github.com/alessandrodefilippo/redundanet
38
- Description-Content-Type: text/markdown
39
-
40
- # RedundaNet - Distributed Encrypted Storage Network
41
-
42
- RedundaNet is a distributed, encrypted storage system built on a secure mesh VPN network. It enables users to contribute storage resources to a collective grid while maintaining privacy through end-to-end encryption.
43
-
44
- ## Features
45
-
46
- - **Decentralized Architecture**: No central authority or single point of failure
47
- - **End-to-End Encryption**: Data is encrypted before leaving the user's device using Tahoe-LAFS
48
- - **GPG-Based Authentication**: Secure node identity verification
49
- - **Private Networking**: Secure Tinc mesh VPN isolates the storage network from the public internet
50
- - **Erasure Coding**: Data is split and distributed across multiple nodes (3-of-10 scheme by default)
51
- - **Resource Sharing**: Users contribute resources and benefit from the collective capacity
52
- - **Containerized Deployment**: Easy setup with Docker Compose
53
- - **Python-Based CLI**: Modern CLI with Typer for easy management
54
-
55
- ## Architecture
56
-
57
- ```mermaid
58
- graph TD
59
- subgraph "User Node"
60
- A[redundanet CLI] --> B[Tahoe Client]
61
- B --> C[Tinc VPN]
62
- end
63
-
64
- subgraph "Storage Node 1"
65
- D[Tinc VPN] --> E[Tahoe Storage]
66
- E --> F[Local Storage]
67
- end
68
-
69
- subgraph "Storage Node 2"
70
- G[Tinc VPN] --> H[Tahoe Storage]
71
- H --> I[Local Storage]
72
- end
73
-
74
- subgraph "Introducer Node"
75
- J[Tinc VPN] --> K[Tahoe Introducer]
76
- end
77
-
78
- C -->|Encrypted VPN| D
79
- C -->|Encrypted VPN| G
80
- C -->|Encrypted VPN| J
81
-
82
- K -.->|Introduction Services| B
83
- K -.->|Introduction Services| E
84
- K -.->|Introduction Services| H
85
- ```
86
-
87
- ## Quick Start
88
-
89
- ### Prerequisites
90
-
91
- - Python 3.11+
92
- - Docker and Docker Compose
93
- - GPG (for key management)
94
-
95
- ### Installation
96
-
97
- #### Using pip (recommended)
98
-
99
- ```bash
100
- pip install redundanet
101
- ```
102
-
103
- #### Using Poetry (for development)
104
-
105
- ```bash
106
- git clone https://github.com/adefilippo83/project-earthgrid.git
107
- cd project-earthgrid
108
- poetry install
109
- ```
110
-
111
- ### Initialize a Node
112
-
113
- ```bash
114
- # Interactive setup
115
- redundanet init
116
-
117
- # Or with options
118
- redundanet init --node-name my-node --vpn-ip 10.100.0.10
119
- ```
120
-
121
- ### Start Services with Docker
122
-
123
- ```bash
124
- # Start as a storage node
125
- docker compose -f docker/docker-compose.yml --profile storage up -d
126
-
127
- # Start as a client only
128
- docker compose -f docker/docker-compose.yml --profile client up -d
129
-
130
- # Start as an introducer (network coordinator)
131
- docker compose -f docker/docker-compose.yml --profile introducer up -d
132
- ```
133
-
134
- ### Check Status
135
-
136
- ```bash
137
- redundanet status
138
- ```
139
-
140
- ## CLI Commands
141
-
142
- ```
143
- redundanet --help
144
-
145
- Commands:
146
- init Initialize a new node
147
- status Show node and network status
148
- sync Sync manifest from repository
149
- validate Validate manifest file
150
-
151
- node Node management commands
152
- list List all nodes in the network
153
- info Show detailed node information
154
- add Add a new node to manifest
155
- remove Remove a node from manifest
156
-
157
- network Network management
158
- join Join an existing network
159
- leave Leave the network
160
- peers Show connected peers
161
- vpn VPN management (start/stop/status)
162
-
163
- storage Storage management
164
- status Show storage status
165
- mount Mount Tahoe filesystem
166
- unmount Unmount filesystem
167
- upload Upload a file
168
- download Download a file
169
- ```
170
-
171
- ## Configuration
172
-
173
- RedundaNet uses a YAML manifest file to define network configuration:
174
-
175
- ```yaml
176
- network:
177
- name: my-network
178
- version: "1.0.0"
179
- domain: redundanet.local
180
- vpn_network: 10.100.0.0/16
181
-
182
- tahoe:
183
- shares_needed: 3
184
- shares_happy: 7
185
- shares_total: 10
186
- introducer_furl: pb://...
187
-
188
- nodes:
189
- - name: node1
190
- internal_ip: 192.168.1.10
191
- vpn_ip: 10.100.0.1
192
- public_ip: 1.2.3.4
193
- gpg_key_id: ABCD1234
194
- roles: [introducer, storage]
195
- storage_contribution: 500GB
196
- ```
197
-
198
- ## Development
199
-
200
- ### Setup Development Environment
201
-
202
- ```bash
203
- # Clone repository
204
- git clone https://github.com/adefilippo83/project-earthgrid.git
205
- cd project-earthgrid
206
-
207
- # Install dependencies
208
- make install
209
-
210
- # Run tests
211
- make test
212
-
213
- # Run linting
214
- make lint
215
-
216
- # Run type checking
217
- make type-check
218
- ```
219
-
220
- ### Project Structure
221
-
222
- ```
223
- redundanet/
224
- ├── src/redundanet/ # Main Python package
225
- │ ├── cli/ # Typer CLI commands
226
- │ ├── core/ # Core business logic
227
- │ ├── vpn/ # Tinc VPN management
228
- │ ├── storage/ # Tahoe-LAFS integration
229
- │ ├── auth/ # GPG authentication
230
- │ ├── network/ # Network utilities
231
- │ └── utils/ # Shared utilities
232
- ├── docker/ # Docker configurations
233
- ├── tests/ # Test suite
234
- ├── docs/ # Documentation
235
- └── manifests/ # Example manifests
236
- ```
237
-
238
- ## Documentation
239
-
240
- - [Installation Guide](docs/installation.md)
241
- - [Quick Start Guide](docs/quickstart.md)
242
- - [Configuration Reference](docs/configuration.md)
243
- - [Architecture Overview](docs/architecture.md)
244
-
245
- ## Contributing
246
-
247
- 1. Fork the repository
248
- 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
249
- 3. Make your changes
250
- 4. Run tests (`make test`)
251
- 5. Run linting (`make lint`)
252
- 6. Commit your changes (`git commit -m 'Add amazing feature'`)
253
- 7. Push to the branch (`git push origin feature/amazing-feature`)
254
- 8. Open a Pull Request
255
-
256
- ## License
257
-
258
- This project is licensed under the GPL License - see the [LICENSE](LICENSE) file for details.
259
-
260
- ## Acknowledgments
261
-
262
- - [Tahoe-LAFS](https://tahoe-lafs.org/) - Distributed storage system
263
- - [Tinc VPN](https://www.tinc-vpn.org/) - Mesh VPN daemon
264
- - [Typer](https://typer.tiangolo.com/) - CLI framework
265
-