aboutsummaryrefslogtreecommitdiffstats
path: root/examples/download.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-05 10:16:28 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-05 10:16:28 +0200
commit41d015137d6d9c097e462cd0662e855c3253d001 (patch)
treee1e5a4e6f1415509c031e079c2b753591975ed05 /examples/download.py
parentfe64864e8cfd2cca3a70ae375a3ea16d537ae6be (diff)
downloadmeshbay-41d015137d6d9c097e462cd0662e855c3253d001.tar.gz
docs: add Python examples for the hub, a node and its control API
examples/ holds small scripts that talk to MeshBay the way the application does, with the values to change as variables at the top of each one: - meshbay_session.py: hub sign-in, WebRTC to a node serving the group, and the handshake (identity bundle, group key, proofs both ways) - list_groups.py: the user's groups, up or down - download.py: one file, decrypted chunk by chunk under a transfer lease - upload.py: one file, sealed in 48 KiB chunks into a group folder - create_group.py: creates a group on the hub and hosts it on the local node through its loopback control API Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'examples/download.py')
-rwxr-xr-xexamples/download.py48
1 files changed, 48 insertions, 0 deletions
diff --git a/examples/download.py b/examples/download.py
new file mode 100755
index 0000000..b5359d2
--- /dev/null
+++ b/examples/download.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+"""Download one file from a MeshBay group, as a member, without a browser."""
+
+import asyncio
+import math
+import sys
+
+from meshbay_common.webcrypto import chunk_key_aes, decrypt_chunk_aes
+from meshbay_session import connect
+
+HUB = "https://meshbay.org"
+USERNAME = "alice"
+PASSPHRASE = "your passphrase"
+GROUP = "Family Photos" # the group's name, as shown in the sidebar
+NODE_ID = "" # empty: the first node online for this group
+FILE_PATH = "shared/2024/beach.jpg" # root name, then folders, then the file name
+OUTPUT = "beach.jpg"
+
+CHUNK_SIZE = 1024 * 1024 # the node serves files in 1 MiB chunks
+
+
+async def main() -> None:
+ session = await connect(HUB, USERNAME, PASSPHRASE, GROUP, NODE_ID)
+
+ entry = next((e for e in await session.index()
+ if f"{e['path']}/{e['name']}" == FILE_PATH), None)
+ if entry is None:
+ sys.exit(f"No file {FILE_PATH!r} in this group")
+
+ # Each chunk is encrypted under its own key, derived from the group key.
+ chunks = max(1, math.ceil(entry["size"] / CHUNK_SIZE))
+ tr = await session.open_transfer("download", entry["size"], chunks)
+ file_hash = bytes.fromhex(entry["id"])
+ with open(OUTPUT, "wb") as out:
+ for i in range(chunks):
+ session.ch.send({"type": "file_req", "v": "0.1", "file_id": entry["id"],
+ "chunk_index": i, "tr": tr})
+ chunk = await session.ch.recv("file_chunk")
+ out.write(decrypt_chunk_aes(chunk_key_aes(session.gek, file_hash, i),
+ chunk["nonce"], chunk["ct"]))
+ print(f"\r{i + 1}/{chunks} chunks", end="", flush=True)
+ session.close_transfer(tr)
+ print(f"\nSaved {OUTPUT} ({entry['size']} bytes)")
+ await session.close()
+
+
+if __name__ == "__main__":
+ asyncio.run(main())