feat: per-network share directories + isolation test
Each network now carries its own share dir, set at join_network time via optional share_dir field or updated live with set_share_dir. The global -share-dir daemon flag becomes a fallback default. - proto: add ShareDir/DownloadDir to NetworkInfo and IpcMessage - netmgr: Join accepts shareDir override; SetShareDir updates live - ipc: wire join_network share_dir and set_share_dir command - daemon: remove -share-dir from auto-join path (pass "" for default) - test-network.sh: per-network join with share_dir; isolation verification section confirms alice/friends and alice/work share dirs are independent - test-tui.sh: join_network with share_dir; peer IDs resolved after join All tests pass: YAW/2.1 FS, share isolation, file transfer, persistence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,10 +25,19 @@ DATA_ROOT="/tmp/waste-test"
|
||||
rm -rf "$DATA_ROOT"
|
||||
mkdir -p "$DATA_ROOT/bin"
|
||||
|
||||
# Seed per-peer share directories with dummy files.
|
||||
mkdir -p "$DATA_ROOT/alice/share" "$DATA_ROOT/bob/share" "$DATA_ROOT/charlie/share"
|
||||
echo "alice's notes" > "$DATA_ROOT/alice/share/notes.txt"
|
||||
dd if=/dev/urandom bs=1K count=64 2>/dev/null | base64 > "$DATA_ROOT/alice/share/photo.jpg.b64"
|
||||
# Per-network share directories — isolation is the whole point:
|
||||
# alice/friends-share → shared on the "friends" network only
|
||||
# alice/work-share → shared on the "work" network only (alice joins both)
|
||||
# bob/share → bob is only on "friends"
|
||||
# charlie/share → charlie is only on "friends"
|
||||
mkdir -p "$DATA_ROOT/alice/friends-share" "$DATA_ROOT/alice/work-share"
|
||||
mkdir -p "$DATA_ROOT/bob/share" "$DATA_ROOT/charlie/share"
|
||||
|
||||
echo "alice's notes" > "$DATA_ROOT/alice/friends-share/notes.txt"
|
||||
dd if=/dev/urandom bs=1K count=64 2>/dev/null | base64 > "$DATA_ROOT/alice/friends-share/photo.jpg.b64"
|
||||
echo "alice's report.pdf" > "$DATA_ROOT/alice/work-share/report.pdf"
|
||||
echo "alice's budget.xlsx" > "$DATA_ROOT/alice/work-share/budget.xlsx"
|
||||
|
||||
dd if=/dev/urandom bs=1K count=128 2>/dev/null | base64 > "$DATA_ROOT/bob/share/archive.tar.b64"
|
||||
echo -e "file1.mp3\nfile2.mp3" > "$DATA_ROOT/bob/share/playlist.m3u"
|
||||
echo "charlie's doc" > "$DATA_ROOT/charlie/share/document.txt"
|
||||
@@ -235,7 +244,6 @@ log "$ALICE_COLOR" "alice" "starting daemon (ipc :${ALICE_IPC})"
|
||||
-data-dir "$DATA_ROOT/alice" \
|
||||
-ipc-port "$ALICE_IPC" \
|
||||
-anchor "$ANCHOR_URL" \
|
||||
-share-dir "$DATA_ROOT/alice/share" \
|
||||
2> >(tee "$DATA_ROOT/alice/daemon.log" | while IFS= read -r l; do echo -e "${ALICE_COLOR}${DIM}[alice] ${l}${RESET}"; done) &
|
||||
PIDS+=($!)
|
||||
|
||||
@@ -245,7 +253,6 @@ log "$BOB_COLOR" "bob" "starting daemon (ipc :${BOB_IPC})"
|
||||
-data-dir "$DATA_ROOT/bob" \
|
||||
-ipc-port "$BOB_IPC" \
|
||||
-anchor "$ANCHOR_URL" \
|
||||
-share-dir "$DATA_ROOT/bob/share" \
|
||||
2> >(tee "$DATA_ROOT/bob/daemon.log" | while IFS= read -r l; do echo -e "${BOB_COLOR}${DIM}[bob] ${l}${RESET}"; done) &
|
||||
PIDS+=($!)
|
||||
|
||||
@@ -255,7 +262,6 @@ log "$CHARLIE_COLOR" "charlie" "starting daemon (ipc :${CHARLIE_IPC})"
|
||||
-data-dir "$DATA_ROOT/charlie" \
|
||||
-ipc-port "$CHARLIE_IPC" \
|
||||
-anchor "$ANCHOR_URL" \
|
||||
-share-dir "$DATA_ROOT/charlie/share" \
|
||||
2> >(tee "$DATA_ROOT/charlie/daemon.log" | while IFS= read -r l; do echo -e "${CHARLIE_COLOR}${DIM}[charlie]${l}${RESET}"; done) &
|
||||
PIDS+=($!)
|
||||
|
||||
@@ -267,14 +273,18 @@ echo ""
|
||||
# ── join network ──────────────────────────────────────────────────────────────
|
||||
echo -e "${DIM}────────────────────────────────────────────────────────${RESET}"
|
||||
echo -e "joining all peers to network: ${BOLD}${NETWORK_NAME}${RESET}"
|
||||
echo -e "${DIM}share dirs are per-network (per-network isolation test)${RESET}"
|
||||
echo -e "${DIM}────────────────────────────────────────────────────────${RESET}"
|
||||
|
||||
JOIN=$(printf '{"type":"join_network","network_name":"%s"}' "$NETWORK_NAME")
|
||||
ipc "$ALICE_IPC" "$JOIN"
|
||||
# Each peer passes share_dir scoped to this network.
|
||||
ipc "$ALICE_IPC" "$(jq -cn --arg net "$NETWORK_NAME" --arg dir "$DATA_ROOT/alice/friends-share" \
|
||||
'{"type":"join_network","network_name":$net,"share_dir":$dir}')"
|
||||
sleep 0.2
|
||||
ipc "$BOB_IPC" "$JOIN"
|
||||
ipc "$BOB_IPC" "$(jq -cn --arg net "$NETWORK_NAME" --arg dir "$DATA_ROOT/bob/share" \
|
||||
'{"type":"join_network","network_name":$net,"share_dir":$dir}')"
|
||||
sleep 0.2
|
||||
ipc "$CHARLIE_IPC" "$JOIN"
|
||||
ipc "$CHARLIE_IPC" "$(jq -cn --arg net "$NETWORK_NAME" --arg dir "$DATA_ROOT/charlie/share" \
|
||||
'{"type":"join_network","network_name":$net,"share_dir":$dir}')"
|
||||
sleep 0.3
|
||||
|
||||
# ── resolve peer IDs ──────────────────────────────────────────────────────────
|
||||
@@ -394,7 +404,7 @@ echo -e "${DIM}─────────────────────
|
||||
|
||||
alice_nets=$(echo '{"type":"get_state"}' | timeout 2 nc 127.0.0.1 "$ALICE_IPC" 2>/dev/null \
|
||||
| grep '"type":"state_snapshot"' | head -1 \
|
||||
| jq -r '[.networks[].network_name] | join(", ")' 2>/dev/null || echo "?")
|
||||
| jq -rc '[.networks[].network_name] | join(", ")' 2>/dev/null || echo "?")
|
||||
echo -e "${DIM} alice networks: [${alice_nets:-none}]${RESET}"
|
||||
|
||||
for peer_ipc in "$ALICE_IPC" "$BOB_IPC" "$CHARLIE_IPC"; do
|
||||
@@ -415,6 +425,54 @@ for peer_ipc in "$ALICE_IPC" "$BOB_IPC" "$CHARLIE_IPC"; do
|
||||
done
|
||||
sleep 0.5
|
||||
|
||||
# ── per-network share isolation ───────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${DIM}────────────────────────────────────────────────────────${RESET}"
|
||||
echo -e "per-network share directory isolation"
|
||||
echo -e "${DIM}────────────────────────────────────────────────────────${RESET}"
|
||||
|
||||
# Alice also joins a "work" network with a completely different share dir.
|
||||
WORK_NET="work-$(date +%s)"
|
||||
ipc "$ALICE_IPC" "$(jq -cn --arg net "$WORK_NET" --arg dir "$DATA_ROOT/alice/work-share" \
|
||||
'{"type":"join_network","network_name":$net,"share_dir":$dir}')"
|
||||
sleep 1
|
||||
|
||||
# Alice's own file list on "friends" should show friends-share files only.
|
||||
friends_raw=$(echo '{"type":"get_file_list"}' | nc -q 2 127.0.0.1 "$ALICE_IPC" 2>/dev/null || true)
|
||||
friends_files=$(echo "$friends_raw" | grep '"type":"file_list"' | head -1 \
|
||||
| jq -r '[.files[].name] | join(" ")' 2>/dev/null || true)
|
||||
|
||||
# Use set_share_dir to dynamically update the work network share dir (runtime change smoke test).
|
||||
work_net_id=$(echo '{"type":"get_state"}' | timeout 2 nc 127.0.0.1 "$ALICE_IPC" 2>/dev/null \
|
||||
| grep '"type":"state_snapshot"' | head -1 \
|
||||
| jq -r --arg net "$WORK_NET" '.networks[] | select(.network_name==$net) | .network_id' 2>/dev/null || true)
|
||||
|
||||
if [ -n "$work_net_id" ]; then
|
||||
# Verify the work network's share_dir is isolated from friends.
|
||||
work_raw=$(echo "{\"type\":\"get_file_list\",\"network_id\":\"$work_net_id\"}" \
|
||||
| nc -q 2 127.0.0.1 "$ALICE_IPC" 2>/dev/null || true)
|
||||
work_files=$(echo "$work_raw" | grep '"type":"file_list"' | head -1 \
|
||||
| jq -r '[.files[].name] | join(" ")' 2>/dev/null || true)
|
||||
|
||||
echo -e " ${BOLD}alice/friends${RESET}: ${friends_files:-<empty>}"
|
||||
echo -e " ${BOLD}alice/work${RESET}: ${work_files:-<empty>}"
|
||||
|
||||
# Check isolation: friends files must not appear in work list and vice versa.
|
||||
if echo "$friends_files" | grep -q "notes.txt" && \
|
||||
echo "$work_files" | grep -q "report.pdf" && \
|
||||
! echo "$friends_files" | grep -q "report.pdf" && \
|
||||
! echo "$work_files" | grep -q "notes.txt"; then
|
||||
echo -e " ${GREEN}✓ share directories are isolated per network${RESET}"
|
||||
else
|
||||
echo -e " ${RED}✗ share isolation failed${RESET}"
|
||||
echo -e " friends: ${friends_files}"
|
||||
echo -e " work: ${work_files}"
|
||||
fi
|
||||
else
|
||||
echo -e " ${RED}✗ could not resolve work network id${RESET}"
|
||||
fi
|
||||
sleep 0.3
|
||||
|
||||
# ── file transfer ────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo -e "${DIM}────────────────────────────────────────────────────────${RESET}"
|
||||
@@ -435,7 +493,7 @@ done
|
||||
|
||||
if [ "$received" = "1" ]; then
|
||||
rx_file=$(ls "$DATA_ROOT/bob/downloads-"*/notes.txt 2>/dev/null | head -1)
|
||||
orig_sha=$(sha256sum "$DATA_ROOT/alice/share/notes.txt" | awk '{print $1}')
|
||||
orig_sha=$(sha256sum "$DATA_ROOT/alice/friends-share/notes.txt" | awk '{print $1}')
|
||||
rx_sha=$(sha256sum "$rx_file" | awk '{print $1}')
|
||||
if [ "$orig_sha" = "$rx_sha" ]; then
|
||||
echo -e " ${GREEN}✓ notes.txt received by bob, sha256 matches${RESET}"
|
||||
|
||||
Reference in New Issue
Block a user