#!/usr/bin/env bash # Drives real MuseHub identity/genealogy calls for Episode 19 ("Identity # Profiles"). Requires the local MuseHub dev stack running. IMPORTANT: per # .museagent.md, always restart + verify freshness of musehub/musehub_worker # before trusting results here if source has changed recently -- a stale # container produced a false crash during this episode's own research. set -euo pipefail HUB="https://localhost:1337" SUFFIX="$(date +%s)" HANDLE_A="ep19-agent-a-$SUFFIX" HANDLE_B="ep19-agent-b-$SUFFIX" gen_key() { python3 -c " from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey import base64, hashlib priv = Ed25519PrivateKey.generate() pub_raw = priv.public_key().public_bytes_raw() print(base64.urlsafe_b64encode(pub_raw).decode().rstrip('=')) print('sha256:' + hashlib.sha256(pub_raw).hexdigest()) print(priv.private_bytes_raw().hex()) " } echo "=== Part 1: register a human-spawned agent (real API call) ===" readarray -t KEY_A < <(gen_key) PUB_A="${KEY_A[0]}"; FP_A="${KEY_A[1]}"; PRIV_A="${KEY_A[2]}" BODY_A="{\"handle\":\"$HANDLE_A\",\"public_key_b64\":\"ed25519:$PUB_A\",\"fingerprint\":\"$FP_A\",\"algorithm\":\"ed25519\",\"agent_model\":\"claude-sonnet-5\",\"label\":\"episode-19-agent-a\"}" printf '%s' "$BODY_A" > /tmp/ep19_reg_a.json HEADER=$(muse sign header --method POST --path "/api/identities/agent" --hub "$HUB" --body-file /tmp/ep19_reg_a.json --json | python3 -c "import json,sys; print(json.load(sys.stdin)['header_value'])") curl -sk -X POST "$HUB/api/identities/agent" -H "Authorization: $HEADER" -H "Content-Type: application/json" --data-binary @/tmp/ep19_reg_a.json | python3 -m json.tool echo echo "=== Part 2: an agent spawning another agent, authenticated with its OWN key ===" readarray -t KEY_B < <(gen_key) PUB_B="${KEY_B[0]}"; FP_B="${KEY_B[1]}" BODY_B="{\"handle\":\"$HANDLE_B\",\"public_key_b64\":\"ed25519:$PUB_B\",\"fingerprint\":\"$FP_B\",\"algorithm\":\"ed25519\",\"agent_model\":\"claude-sonnet-5\",\"label\":\"episode-19-agent-b\"}" printf '%s' "$BODY_B" > /tmp/ep19_reg_b.json python3 - "$PRIV_A" "$HANDLE_A" <<'PY' import sys, time, hashlib, base64, pathlib sys.path.insert(0, str(pathlib.Path.home() / "ecosystem" / "muse")) from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from muse.core.msign import canonical_message priv = Ed25519PrivateKey.from_private_bytes(bytes.fromhex(sys.argv[1])) handle_a = sys.argv[2] body = pathlib.Path("/tmp/ep19_reg_b.json").read_bytes() ts = int(time.time()) msg = canonical_message("POST", "/api/identities/agent", ts, body, host="localhost:1337") sig_b64 = base64.urlsafe_b64encode(priv.sign(msg)).decode().rstrip("=") header = f'MSign handle="{handle_a}" alg="ed25519" ts={ts} sig="{sig_b64}"' pathlib.Path("/tmp/ep19_header_b.txt").write_text(header) PY HEADER=$(cat /tmp/ep19_header_b.txt) curl -sk -X POST "$HUB/api/identities/agent" -H "Authorization: $HEADER" -H "Content-Type: application/json" --data-binary @/tmp/ep19_reg_b.json | python3 -m json.tool echo echo "=== Part 3: the honest gap -- no genealogy query exists for this real chain ===" curl -sk "$HUB/api/openapi.json" | python3 -c " import json, sys d = json.load(sys.stdin) hits = [p for p in d['paths'] if 'genealog' in p.lower() or 'ancestor' in p.lower() or 'root-distance' in p.lower()] print('genealogy-related endpoints found:', hits or 'NONE') " echo echo "=== Part 4: the same real chain, answered directly by the engine that IS correct ===" python3 - "$HANDLE_A" "$HANDLE_B" <<'PY' import sys, pathlib sys.path.insert(0, str(pathlib.Path.home() / "ecosystem" / "musehub")) from musehub.graph.service import IdentityGraphService from musehub.graph.dag import NodeType handle_a, handle_b = sys.argv[1], sys.argv[2] svc = IdentityGraphService() svc.add_identity("gabriel", NodeType.HUMAN) svc.add_identity(handle_a, NodeType.AGENT) svc.add_identity(handle_b, NodeType.AGENT) svc.add_spawn("gabriel", handle_a) svc.add_spawn(handle_a, handle_b) print("distance(gabriel): ", svc.root_distance("gabriel")) print("distance(agent-a): ", svc.root_distance(handle_a)) print("distance(agent-b): ", svc.root_distance(handle_b)) print("human_ancestors(agent-b):", svc.human_ancestors(handle_b)) print() print("--- attempt to close a cycle: agent-b spawns agent-a ---") try: svc.add_spawn(handle_b, handle_a) print("NO ERROR RAISED -- would be a bug") except Exception as e: print(f"Correctly rejected: {type(e).__name__}: {e}") PY echo echo "=== Part 5: attestations -- real infrastructure, empty in this dev DB ===" HEADER=$(muse sign header --method GET --path "/api/profiles/attestation-types" --hub "$HUB" --json | python3 -c "import json,sys; print(json.load(sys.stdin)['header_value'])") curl -sk "$HUB/api/profiles/attestation-types" -H "Authorization: $HEADER" | python3 -m json.tool rm -f /tmp/ep19_reg_a.json /tmp/ep19_reg_b.json /tmp/ep19_header_b.txt echo echo "Demo complete."