trustnet 1.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.
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Thames Senneam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,154 @@
1
+ Metadata-Version: 2.4
2
+ Name: trustnet
3
+ Version: 1.0.0
4
+ Summary: Decentralized cryptographic file & package trust network
5
+ License: MIT
6
+ Project-URL: Homepage, https://github.com/thamessenneam/trustnet
7
+ Project-URL: Issues, https://github.com/thamessenneam/trustnet/issues
8
+ Keywords: security,cryptography,trust,p2p,signing,verification
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: System Administrators
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Security :: Cryptography
19
+ Classifier: Topic :: System :: Networking
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE.txt
23
+ Requires-Dist: cryptography>=41.0.0
24
+ Requires-Dist: Pillow>=10.0.0
25
+ Requires-Dist: zeroconf>=0.131.0
26
+ Dynamic: license-file
27
+
28
+ # TrustNet
29
+
30
+ **Decentralized cryptographic file & package trust network.**
31
+
32
+ Sign any file or folder. Share it. Anyone can verify it hasn't been tampered with — backed by a peer-to-peer network of independent witnesses.
33
+
34
+ ---
35
+
36
+ ## Download
37
+
38
+ | Platform | Installer | Instructions |
39
+ |---|---|---|
40
+ | Windows | [TrustNet-Setup.exe](https://github.com/thamessenneam/TrustNet/releases/latest) | Double-click → Next → Install |
41
+ | macOS | [TrustNet-1.0.0.pkg](https://github.com/thamessenneam/TrustNet/releases/latest) | Double-click → Continue → Install |
42
+ | Linux | [trustnet_1.0.0_amd64.deb](https://github.com/thamessenneam/TrustNet/releases/latest) | `sudo dpkg -i trustnet_1.0.0_amd64.deb` |
43
+
44
+ No Python required. The installer handles everything automatically.
45
+
46
+ Or install via pip (developers):
47
+ ```bash
48
+ pip install trustnet
49
+ ```
50
+
51
+ ---
52
+
53
+ ## What it does
54
+
55
+ - **Sign** any file or folder with your personal Ed25519 key
56
+ - **Verify** files are untampered — locally and across the network
57
+ - **Detect** exactly which files in a folder were modified
58
+ - **Broadcast** your signatures to a decentralized P2P network
59
+ - **Works everywhere** — Windows, macOS, Linux, right-click context menu
60
+
61
+ ---
62
+
63
+ ## How to use
64
+
65
+ ### Right-click (easiest)
66
+ After installing, right-click any file or folder in your file manager:
67
+
68
+ ```
69
+ Any file or folder
70
+ └── TrustNet
71
+ ├── Sign File ← creates a .trustsig alongside it
72
+ └── Verify File ← checks if it was tampered
73
+ ```
74
+
75
+ ### Command line
76
+ ```bash
77
+ trustnet sign myfile.zip # sign a file
78
+ trustnet verify myfile.zip # verify a file
79
+ trustnet sign my_project/ # sign an entire folder
80
+ trustnet verify my_project/ # verify all files in a folder
81
+ trustnet pubkey # show your public key
82
+ ```
83
+
84
+ ### Run the P2P node
85
+ ```bash
86
+ trustnet node start # starts the background node (port 7337)
87
+ trustnet node status # check if it's running
88
+ trustnet node peers # list connected peers
89
+ trustnet node add <ip> # connect to a remote peer
90
+ trustnet node stop # stop the node
91
+ ```
92
+
93
+ Once your node is running, every file you sign is automatically broadcast to all peers. When anyone verifies a file, they see how many independent nodes have attested to it.
94
+
95
+ ---
96
+
97
+ ## How it works
98
+
99
+ ```
100
+ You sign a file
101
+ → Ed25519 signature saved to .trustsig
102
+ → attestation published to your local node
103
+ → node propagates to all known peers
104
+
105
+ Someone verifies the file
106
+ → local: hash re-computed, signature checked
107
+ → network: node queried for independent attestations
108
+ → result: VERIFIED (N signers) or TAMPERED
109
+ ```
110
+
111
+ Every attestation is cryptographically signed. Nodes reject any attestation with an invalid signature — it's impossible to fake one without the signer's private key.
112
+
113
+ ---
114
+
115
+ ## Install
116
+
117
+ ### Windows
118
+ 1. Download `TrustNet-Windows.zip`
119
+ 2. Extract it
120
+ 3. Right-click `install.py` → **Run as administrator**
121
+ 4. Done — right-click any file to see TrustNet
122
+
123
+ ### macOS
124
+ ```bash
125
+ unzip TrustNet-macOS.zip
126
+ cd TrustNet-macOS
127
+ ./install.sh
128
+ ```
129
+
130
+ ### Linux
131
+ ```bash
132
+ unzip TrustNet-Linux.zip
133
+ cd TrustNet-Linux
134
+ ./install.sh
135
+ ```
136
+
137
+ ### Via pip (developers)
138
+ ```bash
139
+ pip install trustnet
140
+ trustnet keygen # generate your key
141
+ ```
142
+
143
+ ---
144
+
145
+ ## First time setup
146
+ ```bash
147
+ trustnet keygen
148
+ ```
149
+ This creates your personal Ed25519 keypair. Your private key stays on your machine — never shared. Your public key is what others use to verify your signatures.
150
+
151
+ ---
152
+
153
+ ## License
154
+ MIT — free to use, modify, and distribute.
@@ -0,0 +1,127 @@
1
+ # TrustNet
2
+
3
+ **Decentralized cryptographic file & package trust network.**
4
+
5
+ Sign any file or folder. Share it. Anyone can verify it hasn't been tampered with — backed by a peer-to-peer network of independent witnesses.
6
+
7
+ ---
8
+
9
+ ## Download
10
+
11
+ | Platform | Installer | Instructions |
12
+ |---|---|---|
13
+ | Windows | [TrustNet-Setup.exe](https://github.com/thamessenneam/TrustNet/releases/latest) | Double-click → Next → Install |
14
+ | macOS | [TrustNet-1.0.0.pkg](https://github.com/thamessenneam/TrustNet/releases/latest) | Double-click → Continue → Install |
15
+ | Linux | [trustnet_1.0.0_amd64.deb](https://github.com/thamessenneam/TrustNet/releases/latest) | `sudo dpkg -i trustnet_1.0.0_amd64.deb` |
16
+
17
+ No Python required. The installer handles everything automatically.
18
+
19
+ Or install via pip (developers):
20
+ ```bash
21
+ pip install trustnet
22
+ ```
23
+
24
+ ---
25
+
26
+ ## What it does
27
+
28
+ - **Sign** any file or folder with your personal Ed25519 key
29
+ - **Verify** files are untampered — locally and across the network
30
+ - **Detect** exactly which files in a folder were modified
31
+ - **Broadcast** your signatures to a decentralized P2P network
32
+ - **Works everywhere** — Windows, macOS, Linux, right-click context menu
33
+
34
+ ---
35
+
36
+ ## How to use
37
+
38
+ ### Right-click (easiest)
39
+ After installing, right-click any file or folder in your file manager:
40
+
41
+ ```
42
+ Any file or folder
43
+ └── TrustNet
44
+ ├── Sign File ← creates a .trustsig alongside it
45
+ └── Verify File ← checks if it was tampered
46
+ ```
47
+
48
+ ### Command line
49
+ ```bash
50
+ trustnet sign myfile.zip # sign a file
51
+ trustnet verify myfile.zip # verify a file
52
+ trustnet sign my_project/ # sign an entire folder
53
+ trustnet verify my_project/ # verify all files in a folder
54
+ trustnet pubkey # show your public key
55
+ ```
56
+
57
+ ### Run the P2P node
58
+ ```bash
59
+ trustnet node start # starts the background node (port 7337)
60
+ trustnet node status # check if it's running
61
+ trustnet node peers # list connected peers
62
+ trustnet node add <ip> # connect to a remote peer
63
+ trustnet node stop # stop the node
64
+ ```
65
+
66
+ Once your node is running, every file you sign is automatically broadcast to all peers. When anyone verifies a file, they see how many independent nodes have attested to it.
67
+
68
+ ---
69
+
70
+ ## How it works
71
+
72
+ ```
73
+ You sign a file
74
+ → Ed25519 signature saved to .trustsig
75
+ → attestation published to your local node
76
+ → node propagates to all known peers
77
+
78
+ Someone verifies the file
79
+ → local: hash re-computed, signature checked
80
+ → network: node queried for independent attestations
81
+ → result: VERIFIED (N signers) or TAMPERED
82
+ ```
83
+
84
+ Every attestation is cryptographically signed. Nodes reject any attestation with an invalid signature — it's impossible to fake one without the signer's private key.
85
+
86
+ ---
87
+
88
+ ## Install
89
+
90
+ ### Windows
91
+ 1. Download `TrustNet-Windows.zip`
92
+ 2. Extract it
93
+ 3. Right-click `install.py` → **Run as administrator**
94
+ 4. Done — right-click any file to see TrustNet
95
+
96
+ ### macOS
97
+ ```bash
98
+ unzip TrustNet-macOS.zip
99
+ cd TrustNet-macOS
100
+ ./install.sh
101
+ ```
102
+
103
+ ### Linux
104
+ ```bash
105
+ unzip TrustNet-Linux.zip
106
+ cd TrustNet-Linux
107
+ ./install.sh
108
+ ```
109
+
110
+ ### Via pip (developers)
111
+ ```bash
112
+ pip install trustnet
113
+ trustnet keygen # generate your key
114
+ ```
115
+
116
+ ---
117
+
118
+ ## First time setup
119
+ ```bash
120
+ trustnet keygen
121
+ ```
122
+ This creates your personal Ed25519 keypair. Your private key stays on your machine — never shared. Your public key is what others use to verify your signatures.
123
+
124
+ ---
125
+
126
+ ## License
127
+ MIT — free to use, modify, and distribute.
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "trustnet"
7
+ version = "1.0.0"
8
+ description = "Decentralized cryptographic file & package trust network"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ keywords = ["security", "cryptography", "trust", "p2p", "signing", "verification"]
13
+ classifiers = [
14
+ "Development Status :: 4 - Beta",
15
+ "Intended Audience :: Developers",
16
+ "Intended Audience :: System Administrators",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Security :: Cryptography",
24
+ "Topic :: System :: Networking",
25
+ ]
26
+ dependencies = [
27
+ "cryptography>=41.0.0",
28
+ "Pillow>=10.0.0",
29
+ "zeroconf>=0.131.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/thamessenneam/trustnet"
34
+ Issues = "https://github.com/thamessenneam/trustnet/issues"
35
+
36
+ [project.scripts]
37
+ trustnet = "trustnet:main"
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["."]
41
+ include = ["trustnet*"]
42
+
43
+ [tool.setuptools.package-data]
44
+ "trustnet.assets" = ["*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ VERSION = "1.0.0"
@@ -0,0 +1,293 @@
1
+ """Command-line interface for TrustNet."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+
11
+ from trustnet.network import DEFAULT_PORT
12
+ from trustnet.core import (
13
+ fingerprint,
14
+ generate_keypair,
15
+ get_config_dir,
16
+ get_public_key_b64,
17
+ sign_directory,
18
+ sign_file,
19
+ verify_directory,
20
+ verify_file,
21
+ )
22
+
23
+
24
+ def _fmt_time(ts: int) -> str:
25
+ return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(ts)) if ts else "unknown"
26
+
27
+
28
+ def cmd_sign(args: argparse.Namespace) -> int:
29
+ path = Path(args.path)
30
+ try:
31
+ if path.is_dir():
32
+ result = sign_directory(path)
33
+ print(f"[TrustNet] Directory signed.")
34
+ print(f" Directory : {result['directory']}")
35
+ print(f" Files : {result['file_count']}")
36
+ print(f" Root hash : {result['root_hash'][:16]}...")
37
+ print(f" Manifest : {result['manifest_file']}")
38
+ print(f" Key : {result['fingerprint']}")
39
+ else:
40
+ result = sign_file(path)
41
+ print(f"[TrustNet] File signed.")
42
+ print(f" File : {result['file']}")
43
+ print(f" SHA-256 : {result['hash'][:16]}...")
44
+ print(f" Signature : {result['sig_file']}")
45
+ print(f" Key : {result['fingerprint']}")
46
+
47
+ if args.json:
48
+ print(json.dumps(result, indent=2))
49
+ return 0
50
+ except Exception as e:
51
+ print(f"[TrustNet] Error: {e}", file=sys.stderr)
52
+ return 1
53
+
54
+
55
+ def cmd_verify(args: argparse.Namespace) -> int:
56
+ path = Path(args.path)
57
+ try:
58
+ if path.is_dir():
59
+ result = verify_directory(path)
60
+ else:
61
+ result = verify_file(path)
62
+
63
+ status = result["status"]
64
+ ok = result["success"]
65
+ symbol = "[OK]" if ok else "[FAIL]"
66
+ print(f"[TrustNet] {symbol} {status}")
67
+ print(f" {result['message']}")
68
+ print(f" Key : {result.get('fingerprint', '-')}")
69
+ print(f" Signed at : {_fmt_time(result.get('timestamp', 0))}")
70
+
71
+ # Network trust info
72
+ n_atts = result.get("network_attestations", 0)
73
+ n_signers = result.get("network_signers", [])
74
+ if result.get("network_online"):
75
+ print(f" Network : {n_atts} attestation(s) from {len(n_signers)} unique signer(s)")
76
+ for s in n_signers:
77
+ print(f" - {s}")
78
+ else:
79
+ print(f" Network : offline (run: trustnet node start)")
80
+
81
+ if not ok and result.get("changed_files"):
82
+ print(f" Changed files ({len(result['changed_files'])}):")
83
+ for f in result["changed_files"][:10]:
84
+ print(f" - {f}")
85
+
86
+ if args.json:
87
+ print(json.dumps(result, indent=2))
88
+
89
+ return 0 if ok else 2
90
+ except Exception as e:
91
+ print(f"[TrustNet] Error: {e}", file=sys.stderr)
92
+ return 1
93
+
94
+
95
+ def cmd_keygen(args: argparse.Namespace) -> int:
96
+ config = get_config_dir()
97
+ private_path = config / "private.key"
98
+ if private_path.exists() and not args.force:
99
+ print("[TrustNet] Key already exists.")
100
+ print(f" Location : {config}")
101
+ print(f" Key : {fingerprint(get_public_key_b64())}")
102
+ print(" Use --force to regenerate (this invalidates all existing signatures).")
103
+ return 0
104
+
105
+ if private_path.exists() and args.force:
106
+ private_path.unlink()
107
+ (config / "public.key").unlink(missing_ok=True)
108
+
109
+ generate_keypair()
110
+ pub_b64 = get_public_key_b64()
111
+ print("[TrustNet] New keypair generated.")
112
+ print(f" Location : {config}")
113
+ print(f" Fingerprint: {fingerprint(pub_b64)}")
114
+ print(f" Public key : {pub_b64[:24]}...")
115
+ return 0
116
+
117
+
118
+ def cmd_pubkey(args: argparse.Namespace) -> int:
119
+ try:
120
+ pub_b64 = get_public_key_b64()
121
+ fp = fingerprint(pub_b64)
122
+ if args.json:
123
+ print(json.dumps({"public_key": pub_b64, "fingerprint": fp}))
124
+ else:
125
+ print(f"Fingerprint : {fp}")
126
+ print(f"Public key : {pub_b64}")
127
+ return 0
128
+ except Exception as e:
129
+ print(f"[TrustNet] Error: {e}", file=sys.stderr)
130
+ return 1
131
+
132
+
133
+ def cmd_node(args: argparse.Namespace) -> int:
134
+ from trustnet.network import node as n, DEFAULT_PORT
135
+
136
+ sub = args.node_cmd
137
+
138
+ if sub == "start":
139
+ if n.is_running():
140
+ print("[TrustNet] Node is already running.")
141
+ info = n.get_local_info()
142
+ if info:
143
+ print(f" Port : {info.get('port', DEFAULT_PORT)}")
144
+ print(f" Attestations : {info.get('attestations', 0)}")
145
+ print(f" Peers : {info.get('peers', 0)}")
146
+ return 0
147
+ port = getattr(args, "port", DEFAULT_PORT)
148
+ print(f"[TrustNet] Starting node on port {port}...")
149
+ try:
150
+ n.start_daemon(port)
151
+ print(f"[TrustNet] Node started.")
152
+ print(f" Port : {port}")
153
+ print(f" Other TrustNet nodes on your network will be discovered automatically.")
154
+ except Exception as e:
155
+ print(f"[TrustNet] Failed to start: {e}", file=sys.stderr)
156
+ return 1
157
+ return 0
158
+
159
+ elif sub == "stop":
160
+ if not n.is_running():
161
+ print("[TrustNet] Node is not running.")
162
+ return 0
163
+ if n.stop_daemon():
164
+ print("[TrustNet] Node stopped.")
165
+ else:
166
+ print("[TrustNet] Could not stop node.", file=sys.stderr)
167
+ return 1
168
+ return 0
169
+
170
+ elif sub == "status":
171
+ if n.is_running():
172
+ info = n.get_local_info()
173
+ print("[TrustNet] Node is RUNNING")
174
+ if info:
175
+ print(f" Port : {info.get('port', DEFAULT_PORT)}")
176
+ print(f" Attestations : {info.get('attestations', 0)}")
177
+ print(f" Peers : {info.get('peers', 0)}")
178
+ else:
179
+ print("[TrustNet] Node is STOPPED")
180
+ print(" Run: trustnet node start")
181
+ return 0
182
+
183
+ elif sub == "peers":
184
+ if not n.is_running():
185
+ print("[TrustNet] Node is not running. Start it first: trustnet node start")
186
+ return 1
187
+ from trustnet.network.ledger import Ledger
188
+ db = Ledger()
189
+ peers = db.get_peers()
190
+ if not peers:
191
+ print("[TrustNet] No peers known yet.")
192
+ print(" Peers on your LAN are discovered automatically.")
193
+ print(" Add a remote peer: trustnet node add <host>")
194
+ else:
195
+ print(f"[TrustNet] {len(peers)} peer(s):")
196
+ for p in peers:
197
+ last = _fmt_time(p.get("last_seen", 0))
198
+ sync = _fmt_time(p.get("last_sync", 0))
199
+ print(f" {p['host']}:{p['port']} seen={last} sync={sync}")
200
+ return 0
201
+
202
+ elif sub == "add":
203
+ host = args.host
204
+ port = getattr(args, "port", DEFAULT_PORT)
205
+ if not n.is_running():
206
+ print("[TrustNet] Node is not running. Start it first: trustnet node start")
207
+ return 1
208
+ from trustnet.network import client
209
+ info = client.get_info(host, port)
210
+ if not info:
211
+ print(f"[TrustNet] Cannot reach {host}:{port}", file=sys.stderr)
212
+ return 1
213
+ from trustnet.network.ledger import Ledger
214
+ Ledger().add_peer(host, port, info.get("node_id", ""))
215
+ print(f"[TrustNet] Peer added: {host}:{port}")
216
+ print(f" Attestations on that node : {info.get('attestations', 0)}")
217
+ print(" Syncing in background...")
218
+ return 0
219
+
220
+ elif sub == "sync":
221
+ if not n.is_running():
222
+ print("[TrustNet] Node is not running.")
223
+ return 1
224
+ from trustnet.network.ledger import Ledger
225
+ from trustnet.network import client
226
+ from trustnet.network.protocol import verify_attestation
227
+ db = Ledger()
228
+ peers = db.get_peers()
229
+ if not peers:
230
+ print("[TrustNet] No peers to sync with.")
231
+ return 0
232
+ total = 0
233
+ for p in peers:
234
+ atts = client.sync_since(p["host"], p["port"], p.get("last_sync", 0))
235
+ new = sum(1 for a in atts if verify_attestation(a) and db.add(a))
236
+ total += new
237
+ db.update_sync(p["host"], p["port"])
238
+ print(f" {p['host']}:{p['port']} -> {new} new attestation(s)")
239
+ print(f"[TrustNet] Sync complete. {total} new attestation(s) total.")
240
+ return 0
241
+
242
+ print(f"[TrustNet] Unknown node command: {sub}", file=sys.stderr)
243
+ return 1
244
+
245
+
246
+ def build_parser() -> argparse.ArgumentParser:
247
+ parser = argparse.ArgumentParser(
248
+ prog="trustnet",
249
+ description="TrustNet — cryptographic file & package signing",
250
+ )
251
+ parser.add_argument("--json", action="store_true", help="Output JSON")
252
+ sub = parser.add_subparsers(dest="command", required=True)
253
+
254
+ p_sign = sub.add_parser("sign", help="Sign a file or directory")
255
+ p_sign.add_argument("path", help="File or directory to sign")
256
+ p_sign.set_defaults(func=cmd_sign)
257
+
258
+ p_verify = sub.add_parser("verify", help="Verify a file or directory")
259
+ p_verify.add_argument("path", help="File or directory to verify")
260
+ p_verify.set_defaults(func=cmd_verify)
261
+
262
+ p_keygen = sub.add_parser("keygen", help="Generate a new keypair")
263
+ p_keygen.add_argument("--force", action="store_true", help="Overwrite existing key")
264
+ p_keygen.set_defaults(func=cmd_keygen)
265
+
266
+ p_pub = sub.add_parser("pubkey", help="Show your public key and fingerprint")
267
+ p_pub.set_defaults(func=cmd_pubkey)
268
+
269
+ # node subcommand with sub-subcommands
270
+ p_node = sub.add_parser("node", help="Manage the TrustNet P2P node")
271
+ node_sub = p_node.add_subparsers(dest="node_cmd", required=True)
272
+
273
+ ns = node_sub.add_parser("start", help="Start the P2P node daemon")
274
+ ns.add_argument("--port", type=int, default=DEFAULT_PORT)
275
+
276
+ node_sub.add_parser("stop", help="Stop the node daemon")
277
+ node_sub.add_parser("status", help="Show node status")
278
+ node_sub.add_parser("peers", help="List known peers")
279
+ node_sub.add_parser("sync", help="Force sync with all peers")
280
+
281
+ na = node_sub.add_parser("add", help="Manually add a peer by host")
282
+ na.add_argument("host", help="Peer hostname or IP address")
283
+ na.add_argument("--port", type=int, default=DEFAULT_PORT)
284
+
285
+ p_node.set_defaults(func=cmd_node)
286
+
287
+ return parser
288
+
289
+
290
+ def main(argv: list[str] | None = None) -> int:
291
+ parser = build_parser()
292
+ args = parser.parse_args(argv)
293
+ return args.func(args)