make-identity-episode19-demo.sh bash
106 lines 4.8 KB
Raw
sha256:5472be4fece32b606c308fad9d57295ce327955fb2b87b8beec6ea1626050473 Add published YouTube URL to Episode 00 Sonnet 5 8 hours ago
1 #!/usr/bin/env bash
2 # Drives real MuseHub identity/genealogy calls for Episode 19 ("Identity
3 # Profiles"). Requires the local MuseHub dev stack running. IMPORTANT: per
4 # .museagent.md, always restart + verify freshness of musehub/musehub_worker
5 # before trusting results here if source has changed recently -- a stale
6 # container produced a false crash during this episode's own research.
7 set -euo pipefail
8
9 HUB="https://localhost:1337"
10 SUFFIX="$(date +%s)"
11 HANDLE_A="ep19-agent-a-$SUFFIX"
12 HANDLE_B="ep19-agent-b-$SUFFIX"
13
14 gen_key() {
15 python3 -c "
16 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
17 import base64, hashlib
18 priv = Ed25519PrivateKey.generate()
19 pub_raw = priv.public_key().public_bytes_raw()
20 print(base64.urlsafe_b64encode(pub_raw).decode().rstrip('='))
21 print('sha256:' + hashlib.sha256(pub_raw).hexdigest())
22 print(priv.private_bytes_raw().hex())
23 "
24 }
25
26 echo "=== Part 1: register a human-spawned agent (real API call) ==="
27 readarray -t KEY_A < <(gen_key)
28 PUB_A="${KEY_A[0]}"; FP_A="${KEY_A[1]}"; PRIV_A="${KEY_A[2]}"
29 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\"}"
30 printf '%s' "$BODY_A" > /tmp/ep19_reg_a.json
31 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'])")
32 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
33
34 echo
35 echo "=== Part 2: an agent spawning another agent, authenticated with its OWN key ==="
36 readarray -t KEY_B < <(gen_key)
37 PUB_B="${KEY_B[0]}"; FP_B="${KEY_B[1]}"
38 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\"}"
39 printf '%s' "$BODY_B" > /tmp/ep19_reg_b.json
40 python3 - "$PRIV_A" "$HANDLE_A" <<'PY'
41 import sys, time, hashlib, base64, pathlib
42 sys.path.insert(0, str(pathlib.Path.home() / "ecosystem" / "muse"))
43 from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
44 from muse.core.msign import canonical_message
45
46 priv = Ed25519PrivateKey.from_private_bytes(bytes.fromhex(sys.argv[1]))
47 handle_a = sys.argv[2]
48 body = pathlib.Path("/tmp/ep19_reg_b.json").read_bytes()
49 ts = int(time.time())
50 msg = canonical_message("POST", "/api/identities/agent", ts, body, host="localhost:1337")
51 sig_b64 = base64.urlsafe_b64encode(priv.sign(msg)).decode().rstrip("=")
52 header = f'MSign handle="{handle_a}" alg="ed25519" ts={ts} sig="{sig_b64}"'
53 pathlib.Path("/tmp/ep19_header_b.txt").write_text(header)
54 PY
55 HEADER=$(cat /tmp/ep19_header_b.txt)
56 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
57
58 echo
59 echo "=== Part 3: the honest gap -- no genealogy query exists for this real chain ==="
60 curl -sk "$HUB/api/openapi.json" | python3 -c "
61 import json, sys
62 d = json.load(sys.stdin)
63 hits = [p for p in d['paths'] if 'genealog' in p.lower() or 'ancestor' in p.lower() or 'root-distance' in p.lower()]
64 print('genealogy-related endpoints found:', hits or 'NONE')
65 "
66
67 echo
68 echo "=== Part 4: the same real chain, answered directly by the engine that IS correct ==="
69 python3 - "$HANDLE_A" "$HANDLE_B" <<'PY'
70 import sys, pathlib
71 sys.path.insert(0, str(pathlib.Path.home() / "ecosystem" / "musehub"))
72 from musehub.graph.service import IdentityGraphService
73 from musehub.graph.dag import NodeType
74
75 handle_a, handle_b = sys.argv[1], sys.argv[2]
76
77 svc = IdentityGraphService()
78 svc.add_identity("gabriel", NodeType.HUMAN)
79 svc.add_identity(handle_a, NodeType.AGENT)
80 svc.add_identity(handle_b, NodeType.AGENT)
81 svc.add_spawn("gabriel", handle_a)
82 svc.add_spawn(handle_a, handle_b)
83
84 print("distance(gabriel): ", svc.root_distance("gabriel"))
85 print("distance(agent-a): ", svc.root_distance(handle_a))
86 print("distance(agent-b): ", svc.root_distance(handle_b))
87 print("human_ancestors(agent-b):", svc.human_ancestors(handle_b))
88
89 print()
90 print("--- attempt to close a cycle: agent-b spawns agent-a ---")
91 try:
92 svc.add_spawn(handle_b, handle_a)
93 print("NO ERROR RAISED -- would be a bug")
94 except Exception as e:
95 print(f"Correctly rejected: {type(e).__name__}: {e}")
96 PY
97
98 echo
99 echo "=== Part 5: attestations -- real infrastructure, empty in this dev DB ==="
100 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'])")
101 curl -sk "$HUB/api/profiles/attestation-types" -H "Authorization: $HEADER" | python3 -m json.tool
102
103 rm -f /tmp/ep19_reg_a.json /tmp/ep19_reg_b.json /tmp/ep19_header_b.txt
104
105 echo
106 echo "Demo complete."
File History 1 commit
sha256:5472be4fece32b606c308fad9d57295ce327955fb2b87b8beec6ea1626050473 Add published YouTube URL to Episode 00 Sonnet 5 8 hours ago