feat(swarm): GET /.well-known/mycelium-node — self-signed advertisement (#87)#91
Merged
Merged
Conversation
…nt (#87) Phase 3c of the Swarm Foundation Plan: serve this node's NodeAdvertisement at the well-known URL per SWARM_SPEC §3.3 / §4.1. First end-to-end wire path — composes Phase 1b (NodeIdentityService.getSelf), Phase 2 (signWithSelfKey), and Phase 3a (WIRE_SPEC_VERSION + NodeAdvertisement type) without modifying any of them. - new mcp-server/src/swarm/endpoints/node-advertisement.ts — pure handler that returns an HTTP-shaped triple (status/headers/body) so the surrounding server is policy-free. - new mcp-server/src/__tests__/node-advertisement-endpoint.test.ts — boots a real http.Server, fetches the endpoint, asserts §4.1 + §4.5 contract: status/content-type/cache-control, multihash(pubkey) === node_id, and signature verifies via Phase 2's verifier against the on-wire pubkey. Plus 503 cases for missing public-url / display-name too long / no self-row. - scripts/dashboard-server.mjs registers the route and a singleton NodeIdentityService — no new server, no new port (issue constraint). - README documents MYCELIUM_PUBLIC_URL (required) and MYCELIUM_DISPLAY_NAME (optional, ≤ 64 chars). Hard constraints honored: unauthenticated, no new web framework, no silent key generation (missing self-row → 503), body is exactly a single NodeAdvertisement with no envelope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 tasks
Dewinator
added a commit
that referenced
this pull request
Apr 29, 2026
Updates the stale "spec-only" header (phases 0–3 have all merged via PRs #79/#81/#82/#85/#89/#91/#92) and pins each phase to its issue + merged commit so a reader can tell at a glance which sections are wired on `main` vs. still paper. Phases 4–9 are deliberately listed as "_not yet issued_" — the project's current priority is *Gehirn perfektionieren* per CLAUDE.md § Roadmap (Reed 2026-04-26), and the wire contract is frozen at v1.0 so an independent implementer can build a phase-3-equivalent peer today. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator
added a commit
that referenced
this pull request
Apr 29, 2026
The cryptographic foundation of the swarm (SWARM_SPEC v1, Ed25519 signing, JCS canonicalization, wire-validator, .well-known discovery, peer/signed-record storage — PRs #78,#79,#81,#82,#85,#89,#91,#92) was landing on main while the README/MANIFESTO still claimed "pairing/swarm/federation deferred". This commit fixes that mismatch. README (EN+DE): - new "Swarm — federation in flight" section with merged-PR table and a "what is next" subsection pointing to the swarm label - Roadmap rewritten: phase 4-5 from "deferred" to "Phase 1 shipped" - existing /.well-known/mycelium-node block folded into the new section - promo video as a clickable poster near the top, served from a v0.4-swarm-phase-1 GitHub release asset (14 MB H.264 1080p) MANIFESTO (EN+DE): - "What is built today" split into brain core + Swarm Phase 1 - aspirational Tailscale+mTLS / mutual-pairing claims removed; those pieces remain on archive/swarm-deferred as historical reference - "What is not built yet" sharpened to the social layer (verification, reputation, banishment-by-consensus, Sybil resistance) plus micro-transactions Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dewinator
added a commit
that referenced
this pull request
May 3, 2026
…nly Vererbung+federation-transport are deferred Roadmap line 194 lumped "Schwarm + Vererbung + Föderation" as deferred, but §10 mechanics (anti-echo, contradicts gate, diversity, REM-self-audit, inbound POST /swarm/lessons admission) have been wired into active code on main since PRs #91, #121, #128, #131, #148, #154, #155. Issues #137 and #138 carry status snapshots from earlier ticks confirming end-to-end wiring against migrations 080 + 082. Splits item 5 into two distinct lines (Paarung/Vererbung — migrations.deferred 033–036, Cross-Host-Föderation — 038/041) and adds a clarifying paragraph that points to the still-active §10 substrate plus docs/waves.md for the cross-wave view. The closing paragraph now also names what specifically the single brain doesn't know (cross-host trust transport), instead of the catch-all "Schwarm/Föderation". Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
NodeAdvertisementatGET /.well-known/mycelium-nodeper SWARM_SPEC §3.3 + §4.1.NodeIdentityService.getSelf), Phase 2 (signWithSelfKey), and Phase 3a (WIRE_SPEC_VERSION+NodeAdvertisementtype) without modifying any of them — Phase 3b's wire-validator already accepts the same shape.MYCELIUM_PUBLIC_URL(required for participation) andMYCELIUM_DISPLAY_NAME(optional, ≤ 64 chars).Research summary
scripts/dashboard-server.mjs— already lazy-imports compiled MCP-server modules frommcp-server/dist/. Same pattern here, no new server, no new port (MYCELIUM_PUBLIC_URLset by operator points at this listener).NodeIdentityService.getSelf()returns{ node_id, pubkey_b64 }from thenodestable whereis_self=true(single-row partial unique index from migration 070). Re-encoded to unpadded base64url for the wire (§3.3 mandates that variant).signWithSelfKeyalready attachessigned_atto the canonical bytes before signing, so the resulting record hassigned_atinside the signature surface — exactly what rule 5 (signature verify) and rule 7 (clock-skew) need on the receiver.wire-validator.test.ts: fresh Ed25519 keypair per test, last 32 bytes of the SPKI DER are the raw pubkey,computeNodeId(pubkeyRaw)is the node_id. Saved a redundant multihash helper that way.What this does NOT do
/swarm/peers,/swarm/lessons,/swarm/hubs— those are later Phase 3 / 4 issues. (Swarm Phase 3d: migration 071 — peer + signed-record storage (file only, do NOT run) #88 already covers the peer-storage migration.)https://.nodes.is_selfis missing, the endpoint returns 503 with a clear error — silent key generation here would orphan every signature this node has produced.node:httproute added to the existing dashboard server.Constitution affirmation
Touches:
multihash(pubkey) == node_id) plus signature without consulting any registry. No central authority.Pillars 2 (Reproduction), 3 (Swarm intelligence), 4 (Microtransactions), and 5 (Experts) are not touched and remain governed by their respective subsystems. No pillar is weakened.
Test plan
A reviewer can verify with the existing test runner:
The new tests live in
mcp-server/src/__tests__/node-advertisement-endpoint.test.ts. They cover:node:httpserver, hits/.well-known/mycelium-nodevia globalfetch, asserts: status 200,Content-Type: application/json; charset=utf-8,Cache-Control: no-store, body parses as JSON, every requiredNodeAdvertisementfield present,signed_atmatches the §3 ISO 8601 ms-precision UTC pattern,multihash(pubkey) == node_id(via the samecomputeNodeIdhelper Phase 1b introduced), and the signature verifies via Phase 2'sverify()against the on-wire pubkey.MYCELIUM_PUBLIC_URLmissing, no self-row innodes,MYCELIUM_DISPLAY_NAMEover 64 chars.display_nameis unset, the wire body has nodisplay_namekey AND the signature still verifies (i.e. we did not silently include an empty string in the canonical bytes).End-to-end smoke test for an operator with the dashboard running:
🤖 Generated with Claude Code