From 7ead2026ef73f43e4d4e8d1e1c89b88839f7b6ab Mon Sep 17 00:00:00 2001 From: gilesb Date: Fri, 9 Jan 2026 01:22:39 +0000 Subject: [PATCH] Add Bitcoin anchoring via OpenTimestamps Implements merkle tree anchoring for ActivityPub activities with provable Bitcoin timestamps. Activities are batched into merkle trees, submitted to OpenTimestamps (free), and proofs stored on IPFS. Key features: - Merkle tree construction with membership proofs - OpenTimestamps integration for Bitcoin anchoring - JSONL backup file on persistent volume (survives DB wipes) - API endpoints for creating/verifying anchors Co-Authored-By: Claude Opus 4.5 --- server.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server.py b/server.py index d1c3415..32bfd4e 100644 --- a/server.py +++ b/server.py @@ -1848,12 +1848,17 @@ async def record_run(req: RecordRunRequest, user: User = Depends(get_required_us # ===== PHASE 2: IPFS OPERATIONS (blocking, before any DB changes) ===== try: - # Pin all inputs - for inp in input_infos: - ipfs_client.pin_or_raise(inp["ipfs_cid"]) + from concurrent.futures import ThreadPoolExecutor, as_completed - # Pin output - ipfs_client.pin_or_raise(output_ipfs_cid) + # Collect all CIDs to pin (inputs + output) + cids_to_pin = [inp["ipfs_cid"] for inp in input_infos] + [output_ipfs_cid] + + # Pin all in parallel + with ThreadPoolExecutor(max_workers=5) as executor: + futures = {executor.submit(ipfs_client.pin_or_raise, cid): cid for cid in cids_to_pin} + for future in as_completed(futures): + cid = futures[future] + future.result() # Raises IPFSError if failed # Store recipe on IPFS recipe_cid = ipfs_client.add_json(recipe_data)