weed-cli 2.1.0__tar.gz → 2.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {weed_cli-2.1.0 → weed_cli-2.2.0}/PKG-INFO +72 -15
  2. {weed_cli-2.1.0 → weed_cli-2.2.0}/README.md +71 -14
  3. {weed_cli-2.1.0 → weed_cli-2.2.0}/node.py +356 -31
  4. {weed_cli-2.1.0 → weed_cli-2.2.0}/pyproject.toml +1 -1
  5. {weed_cli-2.1.0 → weed_cli-2.2.0}/shell.py +92 -50
  6. weed_cli-2.2.0/tests/test_discovery_relay.py +228 -0
  7. weed_cli-2.2.0/tests/test_timing_challenge.py +175 -0
  8. weed_cli-2.2.0/tests/test_tunnel_failover.py +143 -0
  9. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/test_web_ui_api.py +255 -0
  10. {weed_cli-2.1.0 → weed_cli-2.2.0}/web_ui.py +444 -31
  11. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed.py +96 -26
  12. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/PKG-INFO +72 -15
  13. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/SOURCES.txt +2 -0
  14. weed_cli-2.1.0/tests/test_discovery_relay.py +0 -115
  15. {weed_cli-2.1.0 → weed_cli-2.2.0}/LICENSE +0 -0
  16. {weed_cli-2.1.0 → weed_cli-2.2.0}/dht.py +0 -0
  17. {weed_cli-2.1.0 → weed_cli-2.2.0}/discovery_relay.py +0 -0
  18. {weed_cli-2.1.0 → weed_cli-2.2.0}/lightning_settle.py +0 -0
  19. {weed_cli-2.1.0 → weed_cli-2.2.0}/poc_reputation.py +0 -0
  20. {weed_cli-2.1.0 → weed_cli-2.2.0}/setup.cfg +0 -0
  21. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/test_dht.py +0 -0
  22. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/test_host_live_reload.py +0 -0
  23. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/test_node_manifest.py +0 -0
  24. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/test_orbit_ws.py +0 -0
  25. {weed_cli-2.1.0 → weed_cli-2.2.0}/tests/testutil.py +0 -0
  26. {weed_cli-2.1.0 → weed_cli-2.2.0}/tunnel_relay.py +0 -0
  27. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/dependency_links.txt +0 -0
  28. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/entry_points.txt +0 -0
  29. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/requires.txt +0 -0
  30. {weed_cli-2.1.0 → weed_cli-2.2.0}/weed_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weed-cli
3
- Version: 2.1.0
3
+ Version: 2.2.0
4
4
  Summary: Censorship-resistant video PoC — discovery/hosting/download over signed relay events, a real Kademlia DHT, or a TLS-capable NAT-traversal tunnel
5
5
  License: MIT
6
6
  Keywords: p2p,video,censorship-resistant,discovery,dht,kademlia,nat-traversal
@@ -116,6 +116,10 @@ weed discover --relay http://127.0.0.1:9101
116
116
  weed download <content_hash_prefix> --relay http://127.0.0.1:9101 --out downloaded.mp4
117
117
  weed like <content_hash> --relay http://127.0.0.1:9101
118
118
  weed subscribe <target_pubkey> --relay http://127.0.0.1:9101
119
+
120
+ # --relay is repeatable everywhere: an event goes to every relay named,
121
+ # and this copies anything you signed that one relay has and another lacks
122
+ weed sync-relays --relay http://127.0.0.1:9101 --relay https://relay.example.org
119
123
  ```
120
124
 
121
125
  `--advertise-host` matters once you're off localhost — there's no NAT
@@ -160,15 +164,39 @@ weed> download 7f2477c7ea675004ad5dbab6dc7c44327c724b880cc389807df1965b77966acc
160
164
  `web_ui.py` wraps the same `node.py` functions behind a small stdlib
161
165
  JSON API plus a static, no-build-step frontend — host/discover/download/
162
166
  like/subscribe without memorizing CLI flags. Binds `127.0.0.1` by
163
- default (no auth built — this is a local control surface, not something
164
- meant to face the internet); `--bind 0.0.0.0` widens it at your own
165
- risk and auto-detects your real LAN IP for the printed/scanned QR code.
167
+ default (a local control surface, not something meant to face the
168
+ internet); `--bind 0.0.0.0` widens it and auto-detects your real LAN IP
169
+ for the printed/scanned QR code. Auth is optional: `--auth-token` (bare
170
+ for a generated token, or give one; `$WEED_UI_TOKEN` in Docker) gates
171
+ every API call behind a bearer token. The startup QR and printed URL
172
+ then carry it, so scanning is the login; the page shows an unlock
173
+ prompt if opened any other way; the stream URLs it hands to VLC carry
174
+ it as `?token=`, since a player can't send a cookie.
166
175
 
167
176
  ```bash
168
177
  weed serve # alias for `web`, positional: serve [bind] [port]
169
178
  weed serve 0.0.0.0 8080 # reachable from your phone; prints a scan-to-open QR
179
+ weed web --bind 0.0.0.0 --auth-token # same, behind a generated token (printed + in the QR)
180
+ weed web --bind 0.0.0.0 --tls --auth-token --stream-plain-port 8081
170
181
  ```
171
182
 
183
+ `--stream-token` (with `--auth-token`) adds a second, guest tier: a
184
+ browser opened with that token gets the **party view** and nothing
185
+ else — the live Orbit stream, what's playing, a vote on which
186
+ downloaded track plays next (one vote per person per track, toggleable),
187
+ and whatever links you set. The admin's **Party** tab holds the guest
188
+ link and its QR code, the title and links, the live tally with a ▶ per
189
+ track, and an "auto-play the top vote when a track ends" switch.
190
+ Guests can't host, download, like, subscribe, or read the library.
191
+
192
+ `--stream-plain-port` adds a plain-HTTP listener that serves *only* the
193
+ stream endpoints (the Orbit MJPEG stream and downloaded files), for
194
+ players that can't do TLS with a self-signed cert — a Roku IP-camera
195
+ viewer, a smart TV. Nothing else is served there, and the token still
196
+ applies (`?token=`). In "IP Camera Viewer for Roku", enter the Pi's IP,
197
+ that port, and `/api/orbit-view?token={0}` as the stream URL with the
198
+ token as the login: the app substitutes `{0}` itself.
199
+
172
200
  Includes real HTTP range support (`/api/stream/<job_id>`) so a
173
201
  `<video>` tag can seek a completed download instead of downloading it
174
202
  blind.
@@ -233,6 +261,13 @@ hardcode a threshold):
233
261
  Over real WAN distance (tunneled to a remote box over SSH), the gap is
234
262
  ~1700x and separates cleanly at a single sample — the hard case this
235
263
  PoC stress-tests is two peers that are genuinely close together.
264
+ `node.py`'s real download path runs this after its FETCH-and-verify
265
+ gate: `--timing-rounds` (default 5) nonce challenges per candidate,
266
+ each on a chunk the host hasn't been asked to forward yet (so a relay's
267
+ cache is cold), each timed next to a plain `PRICE` round trip on the
268
+ same socket so the number is a ratio rather than milliseconds. Medians
269
+ break ties in the auction; `--max-timing-ratio` turns it into a hard
270
+ gate.
236
271
 
237
272
  **`poc_reputation.py`** — persistent local reputation plus signed,
238
273
  portable attestations (Ed25519, real signing/verification): a client's
@@ -321,12 +356,18 @@ downloader `CONNECT`s; the relay asks the host to dial back
321
356
  sockets, no opinion on the tunneled protocol. Supports TLS at the edge
322
357
  (`tls://` prefix, for relays like Fly that terminate TLS themselves) and
323
358
  a periodic heartbeat so idle control connections survive proxies that
324
- reset connections after a few minutes of silence.
359
+ reset connections after a few minutes of silence. `--tunnel` is
360
+ repeatable: the host registers with every relay named, the publish
361
+ event lists them all in order, and a downloader tries each in turn —
362
+ probing with a real `INFO` round trip, since a relay accepts any
363
+ `CONNECT` and only afterwards says whether it knows the host — so one
364
+ tunnel relay being down (or restarted, or having never heard of this
365
+ host) is a skipped entry, not a failed download.
325
366
 
326
367
  ```bash
327
368
  python3 discovery_relay.py 9101
328
369
  python3 tunnel_relay.py 9199
329
- weed host real_archive --port 9201 --tunnel 127.0.0.1:9199 \
370
+ weed host real_archive --port 9201 --tunnel 127.0.0.1:9199 --tunnel tls://tunnel.example.org:9199 \
330
371
  --relay http://127.0.0.1:9101 --advertise-host 10.255.255.1
331
372
  weed download <content_hash> --relay http://127.0.0.1:9101 --out downloaded.mp4
332
373
  ```
@@ -379,10 +420,20 @@ just designed:
379
420
  Honest edges that are still real constraints even though the core
380
421
  mechanisms hold up:
381
422
 
382
- - Loopback timing separation isn't airtight on a single sample
383
- averaging repeated challenges is required.
384
- - Relay death loses anything posted exclusively there; redundancy
385
- across relays isn't automatic.
423
+ - The timing challenge is now on the real download path (`node.
424
+ nonce_challenge`: five nonce-salted `CHALLENGE` rounds per candidate,
425
+ each timed against a bytes-free `PRICE` round trip on the same socket,
426
+ medians, reported as a ratio) — but the verdict is still yours. The
427
+ ratio breaks ties in the auction and `--max-timing-ratio` can reject
428
+ on it; there's no default cutoff because the honest-vs-relay
429
+ crossover depends on how close the two are (loopback vs LAN vs WAN).
430
+ Measure it live, then pick a number.
431
+ - Relays still never talk to each other. Every event a node signs now
432
+ goes to every relay it names, the web UI mirrors its own events across
433
+ its relays every few minutes, and `weed sync-relays` does the same on
434
+ demand — but that redundancy is per node, scoped to that node's own
435
+ events by default (`--all` mirrors everyone's). An event whose signer
436
+ only ever named one relay, and never syncs, still dies with it.
386
437
  - Lightning settlement is regtest-only, and both sides still have to
387
438
  name which of exactly two demo LND identities (`alice`/`bob`) they
388
439
  are — the protocol pays whoever really won, but the pool of real
@@ -390,8 +441,14 @@ mechanisms hold up:
390
441
  an arbitrary host's own independently-run LND node.
391
442
  - The DHT covers host-discovery only, not the richer publish/like/
392
443
  subscribe/attestation event system.
393
- - The tunnel relay (even with TLS) is a single point of failure and
394
- bandwidth cost, with no redundancy story the way discovery relays have.
395
- - The web UI has no authentication; it's local-only by design. It also
396
- doesn't expose `--lightning-node` in its Host/Download forms yet,
397
- even though the API accepts it.
444
+ - A host can register with several tunnel relays and downloaders fail
445
+ over between them, but each relay is still a bandwidth cost the host
446
+ can't avoid every tunneled byte crosses it and an active download
447
+ doesn't migrate if its relay dies mid-transfer; it restarts on the
448
+ next one.
449
+ - The web UI's auth is two shared bearer tokens (`--auth-token` for
450
+ everything, `--stream-token` for the guest party view), off by default
451
+ and local-only when off. No accounts, no rotation without a restart,
452
+ and votes are anonymous per browser cookie — enough to put a
453
+ LAN-bound UI behind something and hand guests a safe subset, not a
454
+ reason to face it at the internet.
@@ -84,6 +84,10 @@ weed discover --relay http://127.0.0.1:9101
84
84
  weed download <content_hash_prefix> --relay http://127.0.0.1:9101 --out downloaded.mp4
85
85
  weed like <content_hash> --relay http://127.0.0.1:9101
86
86
  weed subscribe <target_pubkey> --relay http://127.0.0.1:9101
87
+
88
+ # --relay is repeatable everywhere: an event goes to every relay named,
89
+ # and this copies anything you signed that one relay has and another lacks
90
+ weed sync-relays --relay http://127.0.0.1:9101 --relay https://relay.example.org
87
91
  ```
88
92
 
89
93
  `--advertise-host` matters once you're off localhost — there's no NAT
@@ -128,15 +132,39 @@ weed> download 7f2477c7ea675004ad5dbab6dc7c44327c724b880cc389807df1965b77966acc
128
132
  `web_ui.py` wraps the same `node.py` functions behind a small stdlib
129
133
  JSON API plus a static, no-build-step frontend — host/discover/download/
130
134
  like/subscribe without memorizing CLI flags. Binds `127.0.0.1` by
131
- default (no auth built — this is a local control surface, not something
132
- meant to face the internet); `--bind 0.0.0.0` widens it at your own
133
- risk and auto-detects your real LAN IP for the printed/scanned QR code.
135
+ default (a local control surface, not something meant to face the
136
+ internet); `--bind 0.0.0.0` widens it and auto-detects your real LAN IP
137
+ for the printed/scanned QR code. Auth is optional: `--auth-token` (bare
138
+ for a generated token, or give one; `$WEED_UI_TOKEN` in Docker) gates
139
+ every API call behind a bearer token. The startup QR and printed URL
140
+ then carry it, so scanning is the login; the page shows an unlock
141
+ prompt if opened any other way; the stream URLs it hands to VLC carry
142
+ it as `?token=`, since a player can't send a cookie.
134
143
 
135
144
  ```bash
136
145
  weed serve # alias for `web`, positional: serve [bind] [port]
137
146
  weed serve 0.0.0.0 8080 # reachable from your phone; prints a scan-to-open QR
147
+ weed web --bind 0.0.0.0 --auth-token # same, behind a generated token (printed + in the QR)
148
+ weed web --bind 0.0.0.0 --tls --auth-token --stream-plain-port 8081
138
149
  ```
139
150
 
151
+ `--stream-token` (with `--auth-token`) adds a second, guest tier: a
152
+ browser opened with that token gets the **party view** and nothing
153
+ else — the live Orbit stream, what's playing, a vote on which
154
+ downloaded track plays next (one vote per person per track, toggleable),
155
+ and whatever links you set. The admin's **Party** tab holds the guest
156
+ link and its QR code, the title and links, the live tally with a ▶ per
157
+ track, and an "auto-play the top vote when a track ends" switch.
158
+ Guests can't host, download, like, subscribe, or read the library.
159
+
160
+ `--stream-plain-port` adds a plain-HTTP listener that serves *only* the
161
+ stream endpoints (the Orbit MJPEG stream and downloaded files), for
162
+ players that can't do TLS with a self-signed cert — a Roku IP-camera
163
+ viewer, a smart TV. Nothing else is served there, and the token still
164
+ applies (`?token=`). In "IP Camera Viewer for Roku", enter the Pi's IP,
165
+ that port, and `/api/orbit-view?token={0}` as the stream URL with the
166
+ token as the login: the app substitutes `{0}` itself.
167
+
140
168
  Includes real HTTP range support (`/api/stream/<job_id>`) so a
141
169
  `<video>` tag can seek a completed download instead of downloading it
142
170
  blind.
@@ -201,6 +229,13 @@ hardcode a threshold):
201
229
  Over real WAN distance (tunneled to a remote box over SSH), the gap is
202
230
  ~1700x and separates cleanly at a single sample — the hard case this
203
231
  PoC stress-tests is two peers that are genuinely close together.
232
+ `node.py`'s real download path runs this after its FETCH-and-verify
233
+ gate: `--timing-rounds` (default 5) nonce challenges per candidate,
234
+ each on a chunk the host hasn't been asked to forward yet (so a relay's
235
+ cache is cold), each timed next to a plain `PRICE` round trip on the
236
+ same socket so the number is a ratio rather than milliseconds. Medians
237
+ break ties in the auction; `--max-timing-ratio` turns it into a hard
238
+ gate.
204
239
 
205
240
  **`poc_reputation.py`** — persistent local reputation plus signed,
206
241
  portable attestations (Ed25519, real signing/verification): a client's
@@ -289,12 +324,18 @@ downloader `CONNECT`s; the relay asks the host to dial back
289
324
  sockets, no opinion on the tunneled protocol. Supports TLS at the edge
290
325
  (`tls://` prefix, for relays like Fly that terminate TLS themselves) and
291
326
  a periodic heartbeat so idle control connections survive proxies that
292
- reset connections after a few minutes of silence.
327
+ reset connections after a few minutes of silence. `--tunnel` is
328
+ repeatable: the host registers with every relay named, the publish
329
+ event lists them all in order, and a downloader tries each in turn —
330
+ probing with a real `INFO` round trip, since a relay accepts any
331
+ `CONNECT` and only afterwards says whether it knows the host — so one
332
+ tunnel relay being down (or restarted, or having never heard of this
333
+ host) is a skipped entry, not a failed download.
293
334
 
294
335
  ```bash
295
336
  python3 discovery_relay.py 9101
296
337
  python3 tunnel_relay.py 9199
297
- weed host real_archive --port 9201 --tunnel 127.0.0.1:9199 \
338
+ weed host real_archive --port 9201 --tunnel 127.0.0.1:9199 --tunnel tls://tunnel.example.org:9199 \
298
339
  --relay http://127.0.0.1:9101 --advertise-host 10.255.255.1
299
340
  weed download <content_hash> --relay http://127.0.0.1:9101 --out downloaded.mp4
300
341
  ```
@@ -347,10 +388,20 @@ just designed:
347
388
  Honest edges that are still real constraints even though the core
348
389
  mechanisms hold up:
349
390
 
350
- - Loopback timing separation isn't airtight on a single sample
351
- averaging repeated challenges is required.
352
- - Relay death loses anything posted exclusively there; redundancy
353
- across relays isn't automatic.
391
+ - The timing challenge is now on the real download path (`node.
392
+ nonce_challenge`: five nonce-salted `CHALLENGE` rounds per candidate,
393
+ each timed against a bytes-free `PRICE` round trip on the same socket,
394
+ medians, reported as a ratio) — but the verdict is still yours. The
395
+ ratio breaks ties in the auction and `--max-timing-ratio` can reject
396
+ on it; there's no default cutoff because the honest-vs-relay
397
+ crossover depends on how close the two are (loopback vs LAN vs WAN).
398
+ Measure it live, then pick a number.
399
+ - Relays still never talk to each other. Every event a node signs now
400
+ goes to every relay it names, the web UI mirrors its own events across
401
+ its relays every few minutes, and `weed sync-relays` does the same on
402
+ demand — but that redundancy is per node, scoped to that node's own
403
+ events by default (`--all` mirrors everyone's). An event whose signer
404
+ only ever named one relay, and never syncs, still dies with it.
354
405
  - Lightning settlement is regtest-only, and both sides still have to
355
406
  name which of exactly two demo LND identities (`alice`/`bob`) they
356
407
  are — the protocol pays whoever really won, but the pool of real
@@ -358,8 +409,14 @@ mechanisms hold up:
358
409
  an arbitrary host's own independently-run LND node.
359
410
  - The DHT covers host-discovery only, not the richer publish/like/
360
411
  subscribe/attestation event system.
361
- - The tunnel relay (even with TLS) is a single point of failure and
362
- bandwidth cost, with no redundancy story the way discovery relays have.
363
- - The web UI has no authentication; it's local-only by design. It also
364
- doesn't expose `--lightning-node` in its Host/Download forms yet,
365
- even though the API accepts it.
412
+ - A host can register with several tunnel relays and downloaders fail
413
+ over between them, but each relay is still a bandwidth cost the host
414
+ can't avoid every tunneled byte crosses it and an active download
415
+ doesn't migrate if its relay dies mid-transfer; it restarts on the
416
+ next one.
417
+ - The web UI's auth is two shared bearer tokens (`--auth-token` for
418
+ everything, `--stream-token` for the guest party view), off by default
419
+ and local-only when off. No accounts, no rotation without a restart,
420
+ and votes are anonymous per browser cookie — enough to put a
421
+ LAN-bound UI behind something and hand guests a safe subset, not a
422
+ reason to face it at the internet.