weed-cli 1.3.3__tar.gz → 1.4.2__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weed-cli
3
- Version: 1.3.3
3
+ Version: 1.4.2
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
@@ -197,6 +197,22 @@ def load_leaves(archive_dir, root_hash):
197
197
  return json.load(f)
198
198
 
199
199
 
200
+ def resolve_file_path(entry, archive_dir):
201
+ """last_path is recorded at archive time (see ott's own manifest
202
+ writer) and is an absolute path on whatever machine ran `ott add` —
203
+ trusting it unconditionally breaks the moment archive_dir is the same
204
+ content mounted somewhere else (a Docker bind mount at /share instead
205
+ of the original /home/user/share it was archived from, a synced
206
+ folder on another machine, ...), since it's still non-empty and short-
207
+ circuits the `or` before archive_dir is ever considered. Falling back
208
+ whenever last_path doesn't actually exist trusts the directory the
209
+ caller explicitly told us to look in over a possibly-stale hint."""
210
+ last_path = entry.get('last_path')
211
+ if last_path and os.path.exists(last_path):
212
+ return last_path
213
+ return os.path.join(archive_dir, entry['name'])
214
+
215
+
200
216
  def _graceful_close(sock):
201
217
  """Plain sock.close() on an SSL-wrapped socket tears down the TCP
202
218
  connection without ever sending a TLS close_notify -- fine for the
@@ -289,7 +305,7 @@ def run_host_server(archive_dir, file_name, port, bind_host='0.0.0.0', quiet=Fal
289
305
  entries_by_hash = {}
290
306
  for entry in entries:
291
307
  leaves = load_leaves(archive_dir, entry['sha256'])
292
- file_path = entry.get('last_path') or os.path.join(archive_dir, entry['name'])
308
+ file_path = resolve_file_path(entry, archive_dir)
293
309
  if not os.path.exists(file_path):
294
310
  sys.exit(f"archived file not found on disk at {file_path}")
295
311
  entries_by_hash[entry['sha256']] = (entry, leaves, file_path)
@@ -741,8 +757,14 @@ def select_host(candidates, k=3, reputation=None, trust_in_signers=None):
741
757
  print(f" x {c['host']}: failed possession challenge ({k} chunks sampled) — skipping")
742
758
  continue
743
759
  price = get_price(conn)
744
- except OSError as e:
745
- print(f" x {c['host']}: unreachable ({e})")
760
+ except (OSError, ValueError, KeyError) as e:
761
+ # ValueError also catches json.JSONDecodeError: a tunnel CONNECT
762
+ # paired with a dead/gone registration (or any host that closes
763
+ # mid-protocol) reads back '' instead of raising a socket error,
764
+ # and a malformed INFO dict raises KeyError on ['sha256'] above —
765
+ # either way this is one bad candidate, not a reason to abort the
766
+ # whole auction.
767
+ print(f" x {c['host']}: unreachable ({type(e).__name__}: {e})")
746
768
  continue
747
769
  rep_score, rep_why = (reputation.trust_score(c['signer_pubkey'], trust_in_signers)
748
770
  if reputation else (0.5, 'no reputation store'))
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "weed-cli"
7
- version = "1.3.3"
7
+ version = "1.4.2"
8
8
  description = "Censorship-resistant video PoC — discovery/hosting/download over signed relay events, a real Kademlia DHT, or a TLS-capable NAT-traversal tunnel"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -183,7 +183,7 @@ class WeedShell(cmd.Cmd):
183
183
  relay_host, relay_port, use_tls = node._parse_tunnel(tunnel)
184
184
  expanded_dir = os.path.expanduser(archive_dir)
185
185
  for entry in entries:
186
- file_path = entry.get('last_path') or os.path.join(expanded_dir, entry['name'])
186
+ file_path = node.resolve_file_path(entry, expanded_dir)
187
187
  tt = threading.Thread(target=_bg,
188
188
  args=(node.run_host_tunnel, relay_host, relay_port,
189
189
  entry['sha256'], entry, all_leaves[entry['sha256']],
@@ -190,29 +190,40 @@ def _run_host_job(host_id, archive_dir, file_name, port, price, relay_urls, adve
190
190
  try:
191
191
  with _quiet():
192
192
  identity = _identity()
193
- entry = node.find_manifest_entry(archive_dir, file_name)
193
+ # every distinct file in the archive, not just the last-added one
194
+ # — see load_manifest_entries' own docstring for why
195
+ # find_manifest_entry (singular) silently dropped every file but
196
+ # one out of a multi-file archive_dir, same bug shell.py's
197
+ # do_host already had fixed
198
+ entries = node.load_manifest_entries(archive_dir, file_name)
194
199
  # fail fast, before announcing anything — a manifest entry with no
195
200
  # matching chunk data would otherwise get announced to the relay
196
201
  # and only fail later, deep in a background thread with no way
197
202
  # for the UI to ever find out
198
- leaves = node.load_leaves(archive_dir, entry['sha256'])
203
+ all_leaves = {e['sha256']: node.load_leaves(archive_dir, e['sha256']) for e in entries}
199
204
  announced = []
200
- for relay_url in relay_urls:
201
- host_addr = f'{advertise_host}:{port}'
202
- node.publish(identity, relay_url, entry['sha256'], entry['name'], host_addr,
203
- tunnel=tunnel)
204
- announced.append(relay_url)
205
+ for entry in entries:
206
+ for relay_url in relay_urls:
207
+ host_addr = f'{advertise_host}:{port}'
208
+ node.publish(identity, relay_url, entry['sha256'], entry['name'], host_addr,
209
+ tunnel=tunnel)
210
+ announced = relay_urls
211
+ files = [{'name': e['name'], 'content_hash': e['sha256']} for e in entries]
205
212
  with _lock:
206
- _hosts[host_id].update(name=entry['name'], content_hash=entry['sha256'],
213
+ _hosts[host_id].update(files=files, name=files[0]['name'],
214
+ content_hash=files[0]['content_hash'],
207
215
  announced_on=announced, status='running')
208
216
  if tunnel:
217
+ # one control connection per file, each registered under its
218
+ # own content hash — see shell.py do_host's docstring
209
219
  relay_host, relay_port, use_tls = node._parse_tunnel(tunnel)
210
220
  expanded_dir = os.path.expanduser(archive_dir)
211
- file_path = entry.get('last_path') or os.path.join(expanded_dir, entry['name'])
212
- threading.Thread(target=node.run_host_tunnel,
213
- args=(relay_host, relay_port, entry['sha256'], entry, leaves,
214
- file_path, price),
215
- kwargs={'use_tls': use_tls, 'quiet': True}, daemon=True).start()
221
+ for entry in entries:
222
+ file_path = node.resolve_file_path(entry, expanded_dir)
223
+ threading.Thread(target=node.run_host_tunnel,
224
+ args=(relay_host, relay_port, entry['sha256'], entry,
225
+ all_leaves[entry['sha256']], file_path, price),
226
+ kwargs={'use_tls': use_tls, 'quiet': True}, daemon=True).start()
216
227
  node.run_host_server(archive_dir, file_name, port, quiet=True, price=price)
217
228
  except SystemExit as e:
218
229
  with _lock:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: weed-cli
3
- Version: 1.3.3
3
+ Version: 1.4.2
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes