A1 — ID-class bug family (v0.6.1 persona split left network-NodeId comparisons
where posting IDs live). ~14 sites fixed with multi-persona semantics ("author is
one of MY posting identities", not == default):
- revoke_post_access / revoke_circle_access (revocation was broken for every post)
- group key create/add-member/rotate (add-member distribution now works)
- circle profile set/delete/get, encrypted-attachment CEK unwrap
- receipt/comment slot authorship, replication cycle, blob eviction own-tier
- orphaned GroupKeyRequest/Response (0xA1/0xA2) deleted (no send side existed)
A2 — Manifest privacy: AuthorManifest.author_addresses removed from struct AND
signature digest; CdnManifest tree-era fields dropped. Startup migrations re-sign
own manifests, purge unverifiable foreign copies, strip persona fields from legacy
network-id profile rows. Posting identities are now location-anonymous.
A3 — Discovery & first contact:
- Open-slot comments: derivable slot V_x (blake3 from author + slot_binder_nonce)
so strangers pass the CDN comment-verification gate; OpenSlotDecl in FoF gating
- Greetings: HPKE-style sealed GreetingBody (return_path + fresh reply_pubkey),
throwaway outer ID, size-bucketed; messaging-first (Reply/Dismiss, no vouch)
- Registry: frozen canonical registry post + REGISTRY_POST_ID, signed registration
comments (self-certifying deletes), newest-wins per persona, --publish-registry
- Comment TTL: expires_at_ms inside signed digest v2 (context bump), rand 30-365d
ordinary / fixed 30d registrations, expiry sweep on the eviction cycle
- Single accept_incoming_comment() gate wired into all three ingest sites, closing
a pre-existing hole where both pull paths stored comments with no verification
- UI: consent panel (visible/listed/greetings, active choice at first publish),
registry search in Discover, sealed "Say hi" compose, greetings inbox
Security fixes from review: forged-tombstone injection, delete-by-sender-trust,
remote SetPolicy poisoning, stored XSS (escapeHtml quotes, 7 pre-existing sites),
greeting off-switch now revokes prior bios' slots.
No PoW (rejected: inverted cost). Session-relay gating untouched.
201 core tests pass; CLI + desktop build; 3-node integration green.
DEPLOY GATE: wire-incompatible with v0.7.3 — ALPN bump to itsgoin/4 lands in
Iteration B. Do not build/deploy/anchor-swap before that.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LGiPD2cF75mnvneSCjdDC5
135 lines
5.1 KiB
Bash
Executable file
135 lines
5.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# v0.8 A3 integration test — registry + greetings across 3 local nodes.
|
|
#
|
|
# Spec: A3-SPEC §6 multi-node scenario. Run from the repo root after
|
|
# `cargo build -p itsgoin-cli`. Uses FIFOs to drive the interactive REPL.
|
|
#
|
|
# 1. All nodes self-materialize the same registry post ID; node1 also
|
|
# runs the --publish-registry genesis one-shot (debug gate override).
|
|
# 2. node2 registers "Alice rust p2p" → node3 `search rust` finds her.
|
|
# node2 `unregister` → node3 re-search finds nothing.
|
|
# 3. node2's bio has a greeting slot; node3 greets it; node2 sees the
|
|
# unsealed text + real sender persona; node2 replies; node3 sees the
|
|
# reply. node1 (holder) stores only ciphertext.
|
|
# 5. Newest-wins: node2 re-registers; node3 sees exactly one entry.
|
|
# 6. (Optional, ITSGOIN_TEST_TTL_SECS) comment expiry sweep.
|
|
#
|
|
# Exit code 0 = all checks passed. Logs: /tmp/itsgoin-cli{1,2,3}.log
|
|
set -u
|
|
cd "$(dirname "$0")/.."
|
|
BIN=target/debug/itsgoin
|
|
[ -x "$BIN" ] || { echo "build first: cargo build -p itsgoin-cli"; exit 2; }
|
|
|
|
PASS=0; FAIL=0
|
|
check() { # check <desc> <cmd...>
|
|
local desc="$1"; shift
|
|
if "$@" >/dev/null 2>&1; then PASS=$((PASS+1)); echo "PASS: $desc";
|
|
else FAIL=$((FAIL+1)); echo "FAIL: $desc"; fi
|
|
}
|
|
strip_ansi() { sed 's/\x1b\[[0-9;]*m//g' "$1" | grep -v "WARN.*netlink\|WARN.*buffer_tool"; }
|
|
sq() { sqlite3 "/tmp/itsgoin-test$1/itsgoin.db" "$2"; }
|
|
|
|
cleanup() {
|
|
{ exec 13>&- 14>&- 15>&-; } 2>/dev/null || true
|
|
kill $(pgrep -f 'itsgoin.*itsgoin-test') 2>/dev/null
|
|
rm -f /tmp/itsgoin-cmd{1,2,3}
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "== setup =="
|
|
kill $(pgrep -f 'itsgoin.*itsgoin-test') 2>/dev/null; sleep 1
|
|
for i in 1 2 3; do
|
|
rm -rf /tmp/itsgoin-test$i /tmp/itsgoin-cmd$i /tmp/itsgoin-cli$i.log
|
|
mkdir -p /tmp/itsgoin-test$i
|
|
mkfifo /tmp/itsgoin-cmd$i
|
|
done
|
|
|
|
export ITSGOIN_TEST_ALLOW_REGISTRY_GENESIS=1
|
|
|
|
# Genesis one-shot on node1's data dir (debug-gate override).
|
|
"$BIN" /tmp/itsgoin-test1 --bind 127.0.0.1:18411 --publish-registry > /tmp/itsgoin-genesis.log 2>&1
|
|
check "genesis prints registry post id matching shipped constant" \
|
|
bash -c 'g=$(grep registry_post_id /tmp/itsgoin-genesis.log | awk "{print \$2}");
|
|
c=$(grep shipped_constant /tmp/itsgoin-genesis.log | awk "{print \$2}");
|
|
[ -n "$g" ] && [ "$g" = "$c" ]'
|
|
|
|
# Start the three nodes with FIFO-driven stdin.
|
|
for i in 1 2 3; do
|
|
( exec "$BIN" /tmp/itsgoin-test$i --bind 127.0.0.1:1841$i \
|
|
< /tmp/itsgoin-cmd$i > /tmp/itsgoin-cli$i.log 2>&1 ) &
|
|
done
|
|
# Keep FIFO write ends open for the whole run.
|
|
exec 13>/tmp/itsgoin-cmd1 14>/tmp/itsgoin-cmd2 15>/tmp/itsgoin-cmd3
|
|
sleep 6
|
|
|
|
n1_id=$(strip_ansi /tmp/itsgoin-cli1.log | grep -m1 "Node ID:" | awk '{print $3}')
|
|
n2_id=$(strip_ansi /tmp/itsgoin-cli2.log | grep -m1 "Node ID:" | awk '{print $3}')
|
|
echo "node1=$n1_id node2=$n2_id"
|
|
check "all nodes self-materialized the registry post" \
|
|
bash -c 'for i in 1 2 3; do sqlite3 /tmp/itsgoin-test$i/itsgoin.db \
|
|
"SELECT count(*) FROM posts WHERE hex(id) = upper(\"'"$(grep shipped_constant /tmp/itsgoin-genesis.log | awk '{print $2}')"'\")" \
|
|
| grep -q 1 || exit 1; done'
|
|
|
|
echo "== mesh: 2,3 -> 1; 3 -> 2 =="
|
|
echo "connect $n1_id@127.0.0.1:18411" >&14
|
|
echo "connect $n1_id@127.0.0.1:18411" >&15
|
|
sleep 4
|
|
echo "connect $n2_id@127.0.0.1:18412" >&15
|
|
sleep 4
|
|
|
|
echo "== step 2: register / search / unregister =="
|
|
echo "name Alice" >&14
|
|
sleep 3
|
|
echo "register Alice rust p2p" >&14
|
|
sleep 3
|
|
echo "search rust" >&15
|
|
sleep 8
|
|
check "node3 search finds Alice" grep -q "Alice" /tmp/itsgoin-cli3.log
|
|
|
|
echo "unregister" >&14
|
|
sleep 3
|
|
echo "search rust" >&15
|
|
sleep 8
|
|
check "node3 re-search finds nothing after signed delete" \
|
|
bash -c 'tail -20 /tmp/itsgoin-cli3.log | grep -q "no registry matches"'
|
|
|
|
echo "== step 3: greeting roundtrip =="
|
|
# node2's bio post id (latest Profile post) from its DB.
|
|
bio2=$(sq 2 'SELECT lower(hex(id)) FROM posts WHERE visibility_intent = '"'"'"Profile"'"'"' ORDER BY timestamp_ms DESC LIMIT 1')
|
|
check "node2 has a bio post with a greeting slot" \
|
|
bash -c '[ -n "'"$bio2"'" ] && sqlite3 /tmp/itsgoin-test2/itsgoin.db \
|
|
"SELECT fof_gating_json FROM posts WHERE lower(hex(id))=\"'"$bio2"'\"" | grep -q Greeting'
|
|
|
|
echo "name Bob" >&15
|
|
sleep 3
|
|
echo "greet $bio2 hello alice from bob" >&15
|
|
sleep 6
|
|
echo "greetings" >&14
|
|
sleep 4
|
|
check "node2 unseals the greeting text" grep -q "hello alice from bob" /tmp/itsgoin-cli2.log
|
|
|
|
echo "reply 0 hello back bob" >&14
|
|
sleep 6
|
|
echo "greetings" >&15
|
|
sleep 4
|
|
check "node3 unseals the reply via its stored fresh reply key" \
|
|
grep -q "hello back bob" /tmp/itsgoin-cli3.log
|
|
|
|
# Holder-side opacity: node1 never sees plaintext greeting bodies.
|
|
check "node1 stores only ciphertext (no greeting plaintext in db)" \
|
|
bash -c '! sqlite3 /tmp/itsgoin-test1/itsgoin.db \
|
|
"SELECT content FROM comments" | grep -q "hello alice"'
|
|
|
|
echo "== step 5: newest-wins =="
|
|
echo "register Alice rust p2p" >&14
|
|
sleep 2
|
|
echo "register AliceV2 rust mesh" >&14
|
|
sleep 4
|
|
echo "search rust" >&15
|
|
sleep 8
|
|
check "node3 sees exactly one (newest) entry for node2's persona" \
|
|
bash -c 'tail -6 /tmp/itsgoin-cli3.log | grep -c "rust" | grep -q "^1$" && tail -6 /tmp/itsgoin-cli3.log | grep -q AliceV2'
|
|
|
|
echo
|
|
echo "== results: $PASS passed, $FAIL failed =="
|
|
[ "$FAIL" -eq 0 ]
|