Conversation
startMcpHttpServer defaulted host to "localhost" and called httpServer.listen(port, "localhost"), which binds only the first address localhost resolves to. On typical Linux hosts that is ::1, so clients using http://127.0.0.1:<port> (or resolving localhost IPv4-first) got ECONNREFUSED while the daemon was healthy. For the default host, bind 127.0.0.1 first and then ::1 on the same port (including the actual port when 0 is requested). A missing address family (EADDRNOTAVAIL/EAFNOSUPPORT) is tolerated when the other bound; any other error, e.g. EADDRINUSE, closes whatever bound and rejects. An explicit host (--host / QMD_HOST) keeps the single listener. The handle's stop() and httpServer.close() release every listener. Found while maintaining a downstream fork. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012FnLMoqMCftiGsbTeTWEDx
yotamleo
added a commit
to yotamleo/Himmel
that referenced
this pull request
Sep 14, 2026
…mleo fork (#761) ## Why HIMMEL-2882 found upstream `tobi/qmd` had merged both of the local `yotamleo/qmd` fork's carried bugfixes (Windows index-name path normalization, a REST JSON.parse guard) under its own SHAs (`2d5806fb`, `4fde5662`, re-authored by the fork maintainer directly against upstream), collapsing the fork's delta to empty. `_qmd_fork_ref` already pinned `04e4dbd8245c527a88f1a8f0bda547aef9ca81fb`, which **is** upstream `tobi/qmd` main — the fork only mirrored it on branch `himmel-main-upstream-2026-09-13`. This PR drops the fork and points the install directly at `tobi/qmd`, at the **same unchanged pinned SHA** (HIMMEL-3041, open upstream as tobi/qmd#950, owns any future pin move). ## What changed - `scripts/lib/qmd-bin.sh`: `_qmd_fork_repo()` default → `https://github.com/tobi/qmd.git` (was `yotamleo/qmd.git`). A new `_qmd_is_legacy_fork_origin()` helper recognizes the retired fork origin in its exact, no-`.git`, and trailing-slash https forms (the SSH form is a different string and stays refused, same as any other unowned remote). `qmd_install()`'s origin-mismatch guard now migrates via `git remote set-url` instead of refusing when it sees a legacy origin. `QMD_FORK_REPO`/`_REF`/`_DIR` names and the on-disk `qmd-fork` dir name are unchanged (renaming was out of scope). Header comments rewritten. - `scripts/lib/test-qmd-bin.sh`: RED→GREEN for the migration path — new assertions cover the exact legacy URL, its no-`.git` form, a trailing slash (all migrate), and the SSH form (still refused, dir untouched). The stub harness's default origin answer repoints to `tobi/qmd`. - `scripts/upstreams.json`: qmd's `fork` sub-object is dropped entirely (mirrors graphify's HIMMEL-1048 de-fork precedent); `kind`/`mode`/ `tracked_repo`/`synced_base`/`tier` are unchanged. No `version_pin` is added — the pin is a full commit SHA, not a version literal, so `apply-drift-bump.sh` correctly keeps reporting SKIP. - `scripts/test-check-plugin-drift.sh`: qmd's fork-field assertions replaced with `assert 'fork' not in qmd` (same shape as the existing claude-obsidian assertion); comments/summary updated. - `scripts/check-plugin-drift.sh`, `scripts/upstreams/resync-fork.sh`, `.claude/commands/fork-resync.md`: stale header comments / worked examples corrected — qmd is no longer a live fork-block entry, so (as of this PR) **zero** registry entries carry a `fork` block; the `/fork-resync` mechanism itself is untouched and stays ready for a future fork. - `scripts/upstreams/test-apply-drift-bump.sh`: one stale assertion message ("fork SHA pin" → "SHA pin, no version_pin"). - Docs/comments only, no behavior change: `docs/tooling-catalog.md`, `docs/setup/new-machine.md`, `marketplace/plugins/qmd/README.md`, `scripts/setup.ps1`, `scripts/adopt.ps1` (both `.ps1` files delegate the actual clone to `bash scripts/lib/qmd-bin.sh install` — no `.ps1` has behavior tied to the fork URL, only wording). **Confirmed NOT touched (checked, not fork references):** `scripts/himmel-doctor.sh:380` and `scripts/luna-upgrade-all.sh:147` / `docs/internals/environment-gotchas.md:1104` all reference the unrelated `himmel-main-ref` / private-RC install-path guard, not the qmd fork. ## RED → GREEN `scripts/lib/test-qmd-bin.sh`, RED (stub default origin repointed to `tobi/qmd` before the code changed): ``` pass=151 fail=36 ``` (2 targeted new/changed assertions failing as expected, plus ~34 cascading failures in existing owned-dir happy-path tests whose fixtures implicitly relied on the old `yotamleo/qmd` default — expected, since the code's default hadn't moved yet.) GREEN after implementing the default-repo switch + migration branch: ``` [test-qmd-bin] pass=187 fail=0 ``` ## Suite pass table (every suite referencing a changed file, foreground) | Suite | Result | |---|---| | `scripts/lib/test-qmd-bin.sh` | 187 passed, 0 failed | | `scripts/test-check-plugin-drift.sh` | all checks passed | | `scripts/test-himmel-update-axis-b.sh` | 155 passed, 0 failed | | `scripts/upstreams/test-apply-drift-bump.sh` | all checks passed | | `scripts/upstreams/test-apply-tool-upgrade.sh` | all checks passed | | `scripts/upstreams/test-drift-fix-cadence.sh` | all checks passed | | `scripts/upstreams/test-resync-fork.sh` | all checks passed | | `scripts/machine-setup/test-upgrade-rtk.sh` | 46 passed, 0 failed | (Enumerated via `git grep -l upstreams.json` under `scripts/` — every test suite among its consumers, not just the two directly touched.) ## Real-install migration proof (scratch HOME, never the live install) ``` $ HOME=<scratch> git clone --depth 1 https://github.com/yotamleo/qmd.git <scratch>/.himmel/qmd-fork origin: https://github.com/yotamleo/qmd.git HEAD: dbfd0b4736aeaf761d1a16ca8e424f071df8feb9 # fork's old tip $ HOME=<scratch> bash -c 'source scripts/lib/qmd-bin.sh; qmd_install' ... origin is the retired himmel qmd fork ('https://github.com/yotamleo/qmd.git') - migrating to https://github.com/tobi/qmd.git (HIMMEL-3045). ... qmd fork installed and verified (qmd 2.8.3 (04e4dbd)). INSTALL_RC=0 # after: origin: https://github.com/tobi/qmd.git HEAD: 04e4dbd8245c527a88f1a8f0bda547aef9ca81fb # matches the unchanged pin ``` The live `$HOME/.himmel/qmd-fork` install and the running qmd daemon were never touched — this used a throwaway `HOME` env override only. leg-burn: calls=127 avg-ctx=129.7k first-turn=73.3k compactions=2 cost-eq=2454.2k 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012mUdiMH5TPrQh58qnY2PPn <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Updated setup, installation, and synchronization guidance to use the upstream `qmd` project pinned to an immutable commit. - Clarified fork auditing, drift handling, migration procedures, and future synchronization scenarios. - **Maintenance** - Existing installations using the recognized legacy fork URL can migrate to the upstream source automatically. - Unrecognized repository origins remain protected and stop installation rather than being modified. - **Tests** - Added coverage for supported legacy-origin migration and rejection of unsupported SSH origins. - Updated validation to reflect the current de-forked `qmd` configuration. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Impact
qmd mcp --http(and--daemon) listens on only ONE loopback family by default.startMcpHttpServerdefaults the host to"localhost"and callshttpServer.listen(port, "localhost"). That binds only the first addresslocalhostresolves to, which is::1on typical modern Linux hosts. Any client that connects tohttp://127.0.0.1:<port>, or resolveslocalhostIPv4-first, getsECONNREFUSEDwhile the daemon is healthy.Repro
At upstream
main04e4dbd, on a Linux host wherelocalhostresolves to::1first:Fix
When the resolved host is the default
"localhost", bind127.0.0.1first, then::1, with the same request handler and on the same port. When0is requested, the second listener reuses the actual port the first one got.EADDRNOTAVAIL/EAFNOSUPPORT, e.g. IPv6 disabled), the server continues on the listener that bound.EADDRINUSEon either address) closes whatever already bound and rejects, so a port collision still fails loudly.--host/QMD_HOST(127.0.0.1,::1,0.0.0.0, …) keeps today's single listener.stop(), andhandle.httpServer.close(), release every listener.portstays a single number.The origin/Host guard already accepted all three loopback spellings. A unit test now pins that.
Tests
test/mcp-http-loopback.test.ts(new) uses ephemeral ports. The IPv6 cases skip only when the host has no IPv6 loopback. It covers:Host/Originheaders are accepted over each family.QMD_HOST, does not serve::1.stop()andclose()release both families.Results:
src/mcp/server.ts: 6 failed / 4 passed. IPv4 is refused.test/mcp-http-loopback.test.ts+test/mcp-origin-guard.test.ts, 46 passed.bun teston the same files: 46 pass, 0 fail.test:typesandlintare clean.Found while maintaining a downstream fork.