gabriel / muse public
Open #20 Bug enhancement
filed by gabriel human · 47 days ago

Signing identity resolution and proposalType canonical cleanup

0 Anchors
Blast radius
Churn 30d
0 Proposals

Context

MSign authentication uses Ed25519 keys stored in ~/.muse/identity.toml, keyed by hostname. When a caller targets a remote that is on a different host than the repo's default [hub] url (e.g. a named remote pointing to staging.musehub.ai while the repo config says localhost:1337), the signing identity must be resolved against the actual target URL — not the default hub URL.

The canonical call is:

url = _resolve_remote_url(root, remote)          # resolve first
token = get_signing_identity(root, remote_url=url)  # then look up by hostname

Resolving identity before the URL is known causes a wrong-hostname lookup, producing HTTP 401 even when the user is correctly registered on the target remote. This is exactly what caused muse ls-remote staging to fail.


Audit results — all callers of get_signing_identity and fetch_remote_info

File Pattern Status
ls_remote.py Called before URL resolved; now get_signing_identity(root, remote_url=url) post-resolution ✅ Fixed (feat/test-merge-6)
fetch.py get_signing_identity(root, remote_url=url) ✅ Correct
clone.py get_signing_identity(root, remote_url=url) ✅ Correct
pull.py get_signing_identity(root, remote_url=url) ✅ Correct
push.py get_signing_identity(root, remote_url=url) ✅ Correct
release.py get_signing_identity(root, url) (positional, not keyword) ⚠️ Works but inconsistent style — should use remote_url= keyword
domains.py get_signing_identity(repo_root)resolved_hub computed above but not forwarded 🔴 Bug — same class as the ls-remote 401

domains.py bug detail

Around line 796–800:

resolved_hub = hub_url or get_hub_url(repo_root) or "https://musehub.ai"
resolved_hub = resolved_hub.rstrip("/")
_validate_publish_url(resolved_hub)

token = get_signing_identity(repo_root)   # BUG: resolved_hub not forwarded

The resolved_hub URL is known at this point. Not passing it means the identity lookup falls back to whatever the repo's [hub] url config says, which may be a different host.


musehub: proposalType legacy values

Migration 0070 renamed mergeStrategy values (state_overlayoverlay, etc.) but did not touch proposalType. The hub still creates and returns proposals with proposalType: "state_merge". The full set of legacy values in use needs to be enumerated and renamed to canonical form.

Scope: Alembic migration, ProposalType enum, musehub_proposals service, MCP tools, proposal templates, and any test fixtures using the legacy strings.


Phases

Phase 1 — Fix domains.py (muse repo)

Change line ~800 from:

token = get_signing_identity(repo_root)

to:

token = get_signing_identity(repo_root, remote_url=resolved_hub)

Add a regression test matching the TestSigningIdentityForwarding pattern established in tests/test_cmd_ls_remote.py.

Phase 2 — Normalize release.py call style (muse repo)

Change the three positional-arg call sites from:

token = get_signing_identity(root, url)

to:

token = get_signing_identity(root, remote_url=url)

No behavior change — positional order is correct — but explicit keyword makes the intent auditable and prevents future positional-arg drift.

Phase 3 — proposalType canonical rename (musehub repo)

  1. Enumerate all proposalType values currently in the DB and in the codebase.
  2. Write migration 0071: UPDATE musehub_proposals SET proposal_type = CASE ... END WHERE proposal_type IN (...legacy values...).
  3. Update ProposalType enum — remove state_* and domain_* variants, keep only canonical names.
  4. Update service layer (musehub_proposals.py), MCP tools, Jinja templates, and test fixtures.
  5. Verify no legacy strings remain in any Python source or template.

Phase 4 — Cross-host integration test (muse repo)

Add a test (or extend tests/test_wire_localhost.py) that:

  • Configures a repo with a named remote pointing to a URL whose hostname differs from the repo's [hub] url
  • Calls ls-remote, fetch, and domains publish (once Phase 1 lands) against that remote
  • Asserts the signing identity presented to the transport matches the remote hostname, not the default hub hostname
  • Asserts no HTTP 401 is produced

This prevents regression across all callers as new commands are added.

Activity
gabriel opened this issue 47 days ago
No activity yet. Use the CLI to comment.