Skip to content

[W5.9] cli: the transport-interop harness (gate) #24

Closed

Description

@leolara

Unit W5.9 of docs/PLAN.md.

The transport-interop harness in test-plan shape. The gate: the go-libp2p matrix over tcp + /noise + yamux, dial and listen both ways, plus the silent-peer and slow-reader scenarios.

Where the spec lives

  • The unit row: root docs/PLAN.md — the deliverable, the dependencies, the priority.
  • The frozen interfaces: root docs/ARCHITECTURE.md, §3 (the mechanisms) and §7, row 11 (this package).
  • The package's own docs: packages/cli/docs/ARCHITECTURE.md and its PLAN.md — read them first; they refine the root docs and never contradict them.
  • The reference: the spike branch, Interop.lean, scripts/ (ping-interop.sh, interop-matrix.sh) (ARCHITECTURE §7). Read it before you implement. Port its tests, keep the wire bytes; the code shape follows §3, not the spike's internals (§7 preamble, §9).

Your freedom, and its cost

The docs govern, and they are yours to change. Where a better design exists, change it — and change the documents in the same pull request. A contract change starts at the root docs/ARCHITECTURE.md; the package docs follow it in the same change. Code that disagrees with the docs, or package docs that disagree with the root, does not merge.

Definition of done

  • The public surface follows ARCHITECTURE §4; the owned contracts hold.
  • A green matrix run recorded in the issue, the timing JSON in the test-plan's shape, docker confined to the scripts.
  • The carried vector and property tests pass through the Tests suite.
  • lake build, lake test, and lake test -- +RTS -N1 -RTS are green; scripts/lint-imports.sh is green.
  • No sorry, no build warnings (CLAUDE.md, "Strict checking").
  • First submodule under your leaf? Widen the lib's globs in lakefile.toml in the same commit.

Depends on

Every issue is claimable from day one; you build ahead of open dependencies against mocks of their frozen interfaces (RULES.md, "Working ahead of the tree").

Ground rules

  • Comment claim to take the issue; an organiser assigns it. One issue at a time.
  • A pull request within 8 hours of the assignment; drafts count. Link the issue (Closes #N). Miss the window, lose the claim.
  • Target the hackathon26 branch.
  • The license-acceptance line of the PR description stays ticked; commits carry no AI co-author trailers. Two guards fail the PR otherwise.
  • Prose register everywhere: CLAUDE.md, "Writing Style".
  • A contract question the docs do not settle: ask on this issue before you code past it.

Activity

  1. IvanAnishchuk commented on Nov 4, 2026

    @IvanAnishchuk
    Contributor

    The W4.2 pull request (#47) changes the TCP close discipline of root ARCHITECTURE §4.7, which the Connection of net/streams carries to every layer above it.

    • close sends a FIN when no write is in flight, and an RST when one is. On v4.34, a write to a peer that stops reading never completes. libuv's shutdown waits behind it, and the write request holds the socket and its descriptor. A C shim in net/streams ends such a connection, and the peer gets an RST. A failed write follows the same rule.
    • A read that meets the peer's RST answers none, the end-of-stream channel of §3.5. go-libp2p closes every TCP connection with an RST (linger 0, vendor/go-libp2p/p2p/transport/tcp/tcp.go, lines 278 to 281), so a session with a go node ends this way.

    The Connection type and its fields stay the same. The net/streams ARCHITECTURE, "Decisions", records both rules with their sources.

  2. IvanAnishchuk commented on Nov 5, 2026

    @IvanAnishchuk
    Contributor

    claim

  3. IvanAnishchuk commented on Nov 5, 2026

    @IvanAnishchuk
    Contributor

    #63 (W5.1) lands libp2p/identity and touches docs and types this unit builds on. No existing signature changes.

    • Libp2p.Identity exports KeyType, PubKey, PrivKey, PeerId, and Identity.Error. The entry points are PubKey.unmarshal?, PrivKey.unmarshal?, PubKey.marshal, PubKey.verify, PrivKey.sign : ByteArray → ExceptT Error IO ByteArray, PrivKey.toPublic, PeerId.ofPubKey, PeerId.ofBytes?, PeerId.toBytes, and PeerId.ofString?.
    • PeerId derives BEq, DecidableEq, Hashable, Ord, and Repr, as root §3.2 asks. ToString prints the parsed text form. The derived == also compares that form, so code that asks "same peer?" compares a.hash == b.hash.
    • To make those classes derivable, Multiformats.Multihash now derives Hashable and Ord, and the generated multiformats enums (HashCode, ContentCodec, Protocol, Multibase.Base) derive Ord.
    • The peer shape rule stays Multiformats.Multiaddr.validPeerHash; PeerId carries its proof.
  4. added a commit that references this issue on Nov 6, 2026
    8f38430
  5. IvanAnishchuk commented on Nov 6, 2026

    @IvanAnishchuk
    Contributor

    Interface change from W5.2: StreamHandler.handle now takes a StreamContext (the proved remotePeer and the connection's remoteAddr) before the stream. The reason and the full shape are on #15.

    A handler value or a core mock in this unit's PR needs the new argument. A handler that needs neither field writes fun _ s => ….

  6. added a commit that references this issue on Nov 8, 2026
    49a8020
  7. IvanAnishchuk commented on Nov 10, 2026

    @IvanAnishchuk
    Contributor

    The W5.9 matrix is green on the real stack, with no mock under the harness (#88, closing #73).

    Cell Record
    go x go (tcp, noise, yamux) {"handshakePlusOneRTTMillis":6.845,"pingRTTMilllis":0.222}
    lean x go {"handshakePlusOneRTTMillis":52.695,"pingRTTMilllis":1.140}
    go x lean {"handshakePlusOneRTTMillis":135.398,"pingRTTMilllis":0.88}
    lean x lean {"handshakePlusOneRTTMillis":55.356,"pingRTTMilllis":1.385}
    silent-peer: the raw connection closed after 30 s, and a go dialer passed meanwhile {"handshakePlusOneRTTMillis":137.437,"pingRTTMilllis":1.084}
    slow-reader {"stalledAfterBytes":524224,"pingRTTMillis":13.49}

    0 failed.

  8. IvanAnishchuk commented on Nov 10, 2026

    @IvanAnishchuk
    Contributor

    This record replaces the one above. #88 now stacks on #89, which moves ping onto the real core, so the harness runs with no bridge over any mock. The matrix is green on that build.

    Cell Record
    go x go (tcp, noise, yamux) {"handshakePlusOneRTTMillis":6.579,"pingRTTMilllis":0.217}
    lean x go {"handshakePlusOneRTTMillis":49.061,"pingRTTMilllis":0.973}
    go x lean {"handshakePlusOneRTTMillis":138.545,"pingRTTMilllis":0.982}
    lean x lean {"handshakePlusOneRTTMillis":52.583,"pingRTTMilllis":2.580}
    silent-peer: the raw connection closed after 30 s, and a go dialer passed meanwhile {"handshakePlusOneRTTMillis":136.802,"pingRTTMilllis":0.853}
    slow-reader {"stalledAfterBytes":524224,"pingRTTMillis":12.417}

    0 failed.

  9. IvanAnishchuk commented on Nov 10, 2026

    @IvanAnishchuk
    Contributor

    The record above still holds after a rebase of the stack. The commits under #88 changed only suites and docs: the host and protocols suites now run on the fixed-key Noise value and a test clock (root ARCHITECTURE §3.1). The interop binary is byte-identical, SHA-256 cc33582966bd36c8c2377ea029e0bbf1784da5deb77b827886e7c593fdcaf039. Its commit is now 013e7f5 on IvanAnishchuk:seam-interop-stack, in place of 2bdb30a.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions