redundanet 2.0.0__py3-none-any.whl → 2.0.1__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 +2 -2
- redundanet/auth/gpg.py +3 -3
- redundanet/cli/node.py +197 -14
- redundanet/network/__init__.py +2 -2
- redundanet/storage/__init__.py +2 -2
- redundanet/utils/process.py +1 -1
- redundanet/vpn/__init__.py +2 -2
- redundanet-2.0.1.dist-info/METADATA +298 -0
- {redundanet-2.0.0.dist-info → redundanet-2.0.1.dist-info}/RECORD +12 -12
- redundanet-2.0.0.dist-info/METADATA +0 -265
- {redundanet-2.0.0.dist-info → redundanet-2.0.1.dist-info}/LICENSE +0 -0
- {redundanet-2.0.0.dist-info → redundanet-2.0.1.dist-info}/WHEEL +0 -0
- {redundanet-2.0.0.dist-info → redundanet-2.0.1.dist-info}/entry_points.txt +0 -0
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
|
-
"
|
|
11
|
+
"Manifest",
|
|
12
12
|
"NetworkConfig",
|
|
13
13
|
"NodeConfig",
|
|
14
14
|
"TahoeConfig",
|
|
15
|
-
"
|
|
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"
|
|
47
|
-
expires=key.get("expires"
|
|
48
|
-
trust=key.get("trust"
|
|
46
|
+
created=key.get("date"),
|
|
47
|
+
expires=key.get("expires"),
|
|
48
|
+
trust=key.get("trust"),
|
|
49
49
|
)
|
|
50
50
|
|
|
51
51
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
296
|
-
|
|
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
|
-
|
|
300
|
-
|
|
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)
|
redundanet/network/__init__.py
CHANGED
redundanet/storage/__init__.py
CHANGED
redundanet/utils/process.py
CHANGED
redundanet/vpn/__init__.py
CHANGED
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: redundanet
|
|
3
|
+
Version: 2.0.1
|
|
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
|
+
[](https://github.com/adefilippo83/redundanet/actions/workflows/ci.yml)
|
|
43
|
+
[](https://pypi.org/project/redundanet/)
|
|
44
|
+
[](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=
|
|
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=
|
|
4
|
+
redundanet/auth/gpg.py,sha256=SIhlRrZa5b5FWNVXstsG-jkjm8oY2J0TySPA0QtYDac,9080
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
29
|
-
redundanet/vpn/__init__.py,sha256=
|
|
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.
|
|
34
|
-
redundanet-2.0.
|
|
35
|
-
redundanet-2.0.
|
|
36
|
-
redundanet-2.0.
|
|
37
|
-
redundanet-2.0.
|
|
33
|
+
redundanet-2.0.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
34
|
+
redundanet-2.0.1.dist-info/METADATA,sha256=d21IBlfafMZ66fQT4LmTxIifYxKiFF56hcJzeFrvbFw,10939
|
|
35
|
+
redundanet-2.0.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
|
36
|
+
redundanet-2.0.1.dist-info/entry_points.txt,sha256=-0cdQEyYsTHavFdq-k_uws2bxnQrD2Ud0FfllcHGrcw,54
|
|
37
|
+
redundanet-2.0.1.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
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|