Skip to content

feat: peer protocol compatibility check between jack instances#23

Merged
roziscoding merged 2 commits into
mainfrom
feat/peer-protocol-compat-check
Jun 7, 2026
Merged

feat: peer protocol compatibility check between jack instances#23
roziscoding merged 2 commits into
mainfrom
feat/peer-protocol-compat-check

Conversation

@roziscoding

@roziscoding roziscoding commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #8.

Versions jack at 0.1.0 and makes peer-protocol mismatches fail loudly instead of silently returning empty results. The version doubles as the peer-protocol version, and a new authenticated /handshake endpoint lets peers negotiate compatibility at connect time.

Changes

  • Version module (lib/version.ts): SERVER_VERSION + MIN_PEER_PROTOCOL_VERSION (both 0.1.0) and a dependency-free x.y.z comparator (compareVersions, isPeerVersionCompatible).
  • Authenticated GET /handshake: returns { name: 'jack', version }. Mounted behind requireApiKey (the unauthenticated /ping Docker health check is untouched), so a wrong API key still fails loudly at connect time.
  • PeerConnector compatibility gate: init now pings /handshake, reads the response defensively, and throws a clear IncompatiblePeerError (Peer "<name>" runs an incompatible peer-protocol version: expected >= 0.1.0, got <X>) when the peer's version is too low, missing/malformed, or the endpoint 404s (old pre-handshake peers). Connectivity/auth errors (network, timeout, 401) propagate unchanged. The failure surfaces in /servers via the existing initialized/initializationError fields.
  • /servers version field: each peer's reported version is stored (peerVersion getter) and exposed in the /servers output.

Behavior for older peers

Older jacks have no /handshake route, so they 404 → treated as an incompatible protocol version and rejected with the clear mismatch error, exactly as required.

Tests

New: version.test.ts, handshake.test.ts, peer-handshake.test.ts (compatible peer, low version, missing/null version, 404 old peer, 401 auth-not-misreported, and the /servers version contract). Full suite: 193 passing, lint + typecheck clean.

Greptile Summary

This PR introduces a peer-protocol compatibility gate between jack instances: a new authenticated GET /handshake endpoint reports { name, version }, and PeerConnector.runInit() now calls it, rejects peers below MIN_PEER_PROTOCOL_VERSION (0.1.0) with a clear IncompatiblePeerError, and surfaces the negotiated version in /servers.

  • lib/version.ts: Adds SERVER_VERSION, MIN_PEER_PROTOCOL_VERSION, and a dependency-free semver comparator (compareVersions, isPeerVersionCompatible).
  • GET /handshake: Authenticated endpoint (behind requireApiKey) returning the server's identity and version; mounted only when config.jack is present, leaving the unauthenticated /ping health check untouched.
  • PeerConnector handshake gate: Replaces the old /peer/search ping; 404s from pre-0.1.0 peers are converted to IncompatiblePeerError; the negotiated version is stored as peerVersion and exposed in /servers via stringifyPeer.

Confidence Score: 4/5

Safe to merge after addressing the stale peerVersion in /servers after a failed retry.

The handshake gate, version comparator, and error handling are all correct. The one concrete defect is that _peerVersion is never cleared at the start of runInit(): if a peer successfully handshakes, later goes offline, and a retry fails, /servers keeps reporting the old version alongside initialized: false, surfacing inaccurate state to callers.

apps/backend/src/lib/servers/peer.ts — the stale peerVersion reset.

Important Files Changed

Filename Overview
apps/backend/src/lib/servers/peer.ts Adds handshake-based compatibility gate in runInit(); _peerVersion is never reset at retry start so a stale version leaks into /servers after a failed re-initialization.
apps/backend/src/lib/version.ts New module with SERVER_VERSION, MIN_PEER_PROTOCOL_VERSION, compareVersions, and isPeerVersionCompatible; logic is correct and well-tested.
apps/backend/src/app.ts Adds authenticated GET /handshake inside the config.jack block; only mounted when jack is configured, which is intentional but means instances without config.jack silently 404 on handshake.
apps/backend/src/lib/errors/IncompatiblePeerError.ts New error class extending AppError with INCOMPATIBLE_PEER code; straightforward and correct.
apps/backend/src/modules/servers/servers.controllers.ts Adds stringifyPeer to expose peerVersion in /servers; peers array type narrowed to PeerConnector[].
apps/backend/src/tests/peer-handshake.test.ts Comprehensive MSW-backed tests covering compatible peer, low version, missing/null version, 404 old peer, 401 auth, and /servers version contract.
apps/backend/src/tests/handshake.test.ts Tests the /handshake endpoint for valid API key and missing API key; correct.
apps/backend/src/tests/version.test.ts Covers compareVersions and isPeerVersionCompatible including malformed inputs; thorough.

Sequence Diagram

sequenceDiagram
    participant PC as PeerConnector (local)
    participant PE as Peer Jack (remote)
    participant SC as ServersController

    PC->>PE: GET /handshake (X-Api-Key)
    alt 404 (pre-0.1.0 peer)
        PE-->>PC: 404 Not Found
        PC->>PC: throw IncompatiblePeerError(got none)
    else 401 (bad API key)
        PE-->>PC: 401 Unauthorized
        PC->>PC: throw FetchError (propagated as-is)
    else 200 OK
        PE-->>PC: "{ name: jack, version: 0.1.0 }"
        PC->>PC: isPeerVersionCompatible(version)?
        alt version below minimum or missing
            PC->>PC: "throw IncompatiblePeerError(expected >= 0.1.0)"
        else version compatible
            PC->>PC: "_peerVersion = version, isInitialized = true"
        end
    end
    SC->>PC: listServers()
    PC-->>SC: "{ initialized, initializationError, peerVersion }"
    SC-->>SC: "stringifyPeer → { version: peerVersion }"
Loading

Fix All in Claude Code Fix All in Codex

Reviews (2): Last reviewed commit: "fix: compare versions via OR instead of ..." | Re-trigger Greptile

- Add version module (SERVER_VERSION, MIN_PEER_PROTOCOL_VERSION, compareVersions)
- Add authenticated /handshake endpoint returning { name, version }
- Gate PeerConnector init on handshake version check
- Surface peer version in /servers output
- Old/incompatible peers fail init with clear IncompatiblePeerError

Closes #8
Comment thread apps/backend/src/lib/servers/peer.ts
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@roziscoding roziscoding merged commit fb31195 into main Jun 7, 2026
6 checks passed
@roziscoding roziscoding deleted the feat/peer-protocol-compat-check branch June 7, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Peer protocol compatibility check between jack instances

1 participant