"""Seed real attestations using actual registered identities on localhost. Real identities: gabriel — human musehub — org claude-code — agent claude-sonnet-4-6 — agent Run from musehub repo root: python3 scripts/seed_attestations_demo.py """ import asyncio import base64 import hashlib import json from datetime import datetime, timezone import httpx from nacl.signing import SigningKey HUB = "https://localhost:1337" DEMO_REPO = "gabriel/musehub" DEMO_COMMIT = "gabriel/musehub@sha256:" + hashlib.sha256(b"demo-commit-seed").hexdigest() def b64url(b: bytes) -> str: return base64.urlsafe_b64encode(b).rstrip(b"=").decode() def make_keypair(seed_str: str) -> tuple[SigningKey, str]: seed = hashlib.sha256(seed_str.encode()).digest() sk = SigningKey(seed) return sk, f"ed25519:{b64url(bytes(sk.verify_key))}" def sign(priv, attester, subject, claim_json, ts, scope="identity", scope_ref=None): parts = ["ATTEST", attester, subject, claim_json, ts] if scope != "identity" and scope_ref: parts.append(scope_ref) return f"ed25519:{b64url(priv.sign('\n'.join(parts).encode()).signature)}" def make_body(priv, pub, attester, subject, type_key, scope="identity", scope_ref=None): seed_hex = hashlib.sha256(f"{attester}:{subject}:{type_key}:{scope}".encode()).hexdigest()[:8] seed_int = int(seed_hex, 16) % (365 * 24 * 3600) ts = datetime.fromtimestamp( datetime(2026, 1, 1, tzinfo=timezone.utc).timestamp() + seed_int, tz=timezone.utc ).isoformat(timespec="seconds") claim_json = json.dumps({"type": type_key}) body = { "attester": attester, "attesterPublicKey": pub, "subject": subject, "claim": claim_json, "issuedAt": ts, "scope": scope, "signature": sign(priv, attester, subject, claim_json, ts, scope, scope_ref), } if scope_ref: body["scopeRef"] = scope_ref if scope == "commit": body["commitId"] = "sha256:" + hashlib.sha256(f"{attester}{subject}{type_key}".encode()).hexdigest() return body # Keypairs for each real identity (deterministic seeds for reproducibility) KEYS = { "gabriel": make_keypair("gabriel-real-seed"), "musehub": make_keypair("musehub-org-real-seed"), "claude-code": make_keypair("claude-code-real-seed"), "claude-sonnet-4-6": make_keypair("claude-sonnet-real-seed"), } def k(handle): return KEYS[handle] # ── What makes sense ────────────────────────────────────────────────────────── # # RECEIVED by gabriel — others vouching for gabriel # human: musehub (org) and claude-code (agent) confirm gabriel is human # trusted: musehub and claude-sonnet-4-6 stake their rep on gabriel # co-author: claude-code and claude-sonnet-4-6 — they shipped code together # collab: musehub — worked on the platform together # contractor: musehub — gabriel did contracted work for the org # skill:verified: musehub and claude-code have seen the work firsthand # code:reviewed: claude-code reviewed gabriel's code # code:approved: claude-sonnet-4-6 approved a delivery # deploy:approved: claude-code signed off on a deploy # # GIVEN by gabriel — gabriel vouching for others # agent: gabriel confirms claude-code and claude-sonnet-4-6 are legit agents # spawned-by: gabriel spawned claude-code — its actions trace back to him # delegate: gabriel delegated to claude-sonnet-4-6 # trusted: gabriel trusts musehub # org: gabriel confirms musehub is a legitimate org # co-author: gabriel and claude-code co-authored # skill:verified: gabriel has seen claude-code's capabilities firsthand # code:reviewed: gabriel reviewed musehub's code # midi:generated: gabriel confirms claude-sonnet-4-6 generated the MIDI # master:approved: gabriel approved the master # ATTESTATIONS = [ # ── Received by gabriel ────────────────────────────────────────────────── ("musehub", "gabriel", "human", "identity", None), ("claude-code", "gabriel", "human", "identity", None), ("musehub", "gabriel", "trusted", "identity", None), ("claude-sonnet-4-6", "gabriel", "trusted", "identity", None), ("claude-code", "gabriel", "co-author", "identity", None), ("claude-sonnet-4-6", "gabriel", "co-author", "identity", None), ("musehub", "gabriel", "collab", "identity", None), ("musehub", "gabriel", "contractor", "identity", None), ("musehub", "gabriel", "skill:verified", "identity", None), ("claude-code", "gabriel", "skill:verified", "identity", None), ("claude-code", "gabriel", "code:reviewed", "repo", DEMO_REPO), ("claude-sonnet-4-6", "gabriel", "code:approved", "repo", DEMO_REPO), ("claude-code", "gabriel", "deploy:approved","commit", DEMO_COMMIT), # ── Given by gabriel ───────────────────────────────────────────────────── ("gabriel", "claude-code", "agent", "identity", None), ("gabriel", "claude-sonnet-4-6", "agent", "identity", None), ("gabriel", "claude-code", "spawned-by", "identity", None), ("gabriel", "claude-sonnet-4-6", "delegate", "identity", None), ("gabriel", "musehub", "trusted", "identity", None), ("gabriel", "musehub", "org", "identity", None), ("gabriel", "claude-code", "co-author", "identity", None), ("gabriel", "claude-code", "skill:verified", "identity", None), ("gabriel", "musehub", "code:reviewed", "repo", DEMO_REPO), ("gabriel", "claude-sonnet-4-6", "midi:generated", "identity", None), ("gabriel", "musehub", "master:approved","identity", None), ] async def post_one(client, attester, subject, type_key, scope, scope_ref): priv, pub = k(attester) body = make_body(priv, pub, attester, subject, type_key, scope, scope_ref) r = await client.post(f"{HUB}/api/profiles/{subject}/attestations", json=body, timeout=10) ok = r.status_code in (200, 201) status = "✓" if ok else "✗" detail = "" if ok else f" {r.status_code}: {r.text[:80]}" return f" {status} {attester:20s} → {subject:20s} [{type_key}]{detail}" async def main(): print(f"Seeding real attestations on {HUB}\n {len(ATTESTATIONS)} total\n") async with httpx.AsyncClient(verify=False) as client: results = await asyncio.gather(*[post_one(client, *row) for row in ATTESTATIONS]) ok = sum(1 for r in results if "✓" in r) for r in results: print(r) print(f"\n{'✓' if ok == len(results) else '✗'} {ok}/{len(results)} succeeded.") if __name__ == "__main__": asyncio.run(main())