gabriel / muse public
Open #20 Bug enhancement
filed by gabriel human · 1 day 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 1 day ago
No activity yet. Use the CLI to comment.