gabriel / muse public
Closed #100 Enhancement
filed by gabriel human · 35 days ago

No first-party way to retrieve your own already-registered public key (muse auth show/whoami only expose fingerprint)

0 Anchors
— Blast radius
— Churn 30d
0 Proposals

No first-party way to retrieve your own already-registered public key

Background

Surfaced while debugging a muse sign verify command for the Build with Muse episode 04 script. muse sign verify requires --public-key-b64 — the raw base64url Ed25519 public key (no ed25519: prefix) — but the only time that value is ever printed is at muse auth keygen/muse auth register time. If you didn't capture it then, or need it again later (exactly this case: debugging a signing command well after the key was created), there is no command that prints it back to you.

Confirmed no existing path surfaces it:

  • muse auth whoami --json — fingerprint only, no public key.
  • muse auth show --json — fingerprint, algorithm, hd_path, derived paths, AVAX address... no public key.
  • muse auth key list --json — fingerprint, label, timestamps. No public key.
  • muse hub user read <handle> --json — public profile (repos, bio, session credits...). No public key.

The only way to recover it is to re-derive it locally from the keychain mnemonic via resolve_signing_identity() + public_key_to_b64url() — which works, but requires writing a Python snippet against internal modules; there's no first-party CLI surface for it.

Why this matters

The fingerprint (sha256:<hash of the public key>) is one-way — you can't get from a fingerprint back to the public key bytes muse sign verify actually needs. Any workflow that needs to verify an MSign header out-of-band (CI, a pre-receive hook, a third-party tool, or just a human debugging a request by hand, as happened here) needs the actual public key, and today the only source of truth for it is either your own memory of the keygen-time output, or reverse-engineering it from the mnemonic.

Scope

Add a command (or a flag on an existing one) that prints the public key for an already-registered identity:

  • muse auth show --public-key (or a new dedicated field in the existing JSON output) for your own identity.
  • Consider whether the hub should expose a registered public key on a public/authenticated endpoint too (e.g. as part of muse hub user read or a dedicated muse hub user public-key <handle>) — useful for anyone who needs to verify a signature from a different user's identity, not just their own.
  • Decide whether agent identities (--agent-id) need the same lookup (muse auth show --agent-id X --public-key).

Acceptance criteria

  • A documented command prints the exact public key muse sign verify --public-key-b64 expects, for an identity already registered on this machine, without needing to touch internal Python modules or remember keygen-time console output.
◎ Activity1
●
gabriel opened this issue 35 days ago
○
gabriel 2 days ago

Implemented and shipped to source.

muse auth show --public-key (plus --agent-id for agent identities). Resolves via the exact same path muse sign itself uses (get_signing_identity + sign.py's own _public_key_info) rather than a parallel reimplementation — guarantees the printed value is always byte-identical to what muse sign verify actually expects.

muse auth show --hub https://staging.musehub.ai --public-key
jkKZmTkhZ9YBVMQWvIL_DhOT9BnMWyovKrPxTb6gU1M

muse sign verify --public-key-b64 "$(muse auth show --hub https://staging.musehub.ai --public-key)" ...

Testing: 6 new tests — byte-identical output vs. sign.py's own derivation, clean None/error on no identity (no crash), text/JSON round-trips, and a full end-to-end round trip through the real build_msign_header/verify_msign_header. All green. Fixed 4 pre-existing tests that broke from this change (a MagicMock()-as-Namespace hazard: any new attribute access on a MagicMock resolves truthy rather than hitting getattr's default — worth knowing about for any future flag added to run_show).

Real-world verification (not just mocked): ran this against my actual registered staging identity. The printed key's derived fingerprint matched muse sign header's own reported fingerprint exactly. Then the acceptance test itself: signed a real header with muse sign header, verified it with muse sign verify --public-key-b64 using the exact value this command printed — {"valid": true}.

Didn't take up scope items 2/3 from the original ticket (hub-side exposure of other users' public keys; this wasn't required by the acceptance criteria, which was scoped to "an identity already registered on this machine"). Worth a separate ticket if you want that — flagging rather than assuming.

Not yet in a published release — same batching note as #105/#106.

Closing — acceptance criterion met and proven live.

✓
closed this issue 2 days ago