Skip to content

TS6 server-to-server linking - #2458

Draft
kevinpostal wants to merge 7 commits into
ergochat:masterfrom
kevinpostal:pr/ts6-linking
Draft

kevinpostal wants to merge 7 commits into
ergochat:masterfrom
kevinpostal:pr/ts6-linking

Conversation

@kevinpostal

Copy link
Copy Markdown

TS6 server-to-server linking for Ergo, extracted from waveplate/ergo branch ts6-linking (commits 622bc84, dc5c31e, 41b3f27, 3491c03, 0b05e5e, author @waveplate, cherry-picked verbatim; merge commit cc472a9 skipped). Cherry-picks applied cleanly against current master with no conflicts.

Depends on #2456 (stacked; the first commit on this branch is that PR's commit).

What the change contains:

  • irc/s2s_manager.go — link lifecycle: dial/listen, reconnect, routing graph, netsplit cleanup, multi-hop broadcast.
  • irc/s2s_handlers.go — inbound TS6 command handlers (PASS, CAPAB, SERVER, SVINFO, SID, UID/EUID, SJOIN, TMODE, BMASK, TB/TOPIC, JOIN, PART, KICK, MODE, PRIVMSG/NOTICE, AWAY, KILL, ENCAP, PING/PONG, SQUIT) plus nickname/UID resolution and TS collision handling.
  • irc/s2s_burst.go — outbound network-state burst (servers, users, channels, modes, bans, topics).
  • irc/s2s_link.go, irc/s2s_types.go — connection I/O and shared types; small hooks in channel.go, client.go, client_lookup_set.go (UID-indexed client lookup), nickname.go, handlers.go, commands.go, help.go, modes.
  • Config: server.sid (this server's 3-character SID) and a server.links map per peer (name, sid, hostname, port, tls, send-password, receive-password, auto-connect) — see the commented examples in default.yaml/traditional.yaml and the docs/MANUAL.md section added by 0b05e5e.
  • distrib/docker-linking/ (from 41b3f27) — two-server linked docker deployment (compose.yaml, ircd-a.yaml, ircd-b.yaml, README.md) for manual testing.
  • Tests: irc/s2s_test.go — unit + integration suite for handshake, burst, runtime propagation and splits.

Reviewer note — inbound handshake password check (irc/s2s_handlers.go, handlePass, quoted verbatim):

	// Password validation
	if link.inbound {
		// Find matching link config if present
		cfg := s2s.FindLinkConfig(sid, "")
		if cfg != nil && cfg.ReceivePassword != "" {
			if pass != cfg.ReceivePassword {
				return fmt.Errorf("invalid link password for SID %s", sid)
			}
			link.config = cfg
		}
	}

i.e. an inbound connection presenting an unknown SID — one with no matching entry in server.links (matched by SID via FindLinkConfig) — is accepted without any password check (only TS-version, SID-format and self-collision validation apply). Operators must define a links entry for every peer they intend to link with; otherwise anyone can introduce a server into the network. Flagging explicitly so reviewers see it.

Upstream policy note: docs/MANUAL.md currently states "Ergo does not currently support server-to-server linking (federation), meaning that all clients must connect to the same instance." This PR is therefore opened as a draft for discussion, not as a merge-ready proposal.

Testing: make test was run locally but does NOT fully pass on this branch; make irctest was NOT run (needs the irctest harness). Verbatim failure (go test -tags "i18n mysql postgresql sqlite" ./irc/, full package run reports "170 passed, 1 failed"):

--- FAIL: TestCanonicalizeMaskWildcard (0.00s)
    strings_test.go:193: expected <hebrew nick> to canonicalize to <hebrew nick>!*@*, instead
    strings_test.go:196: expected <hebrew nick> to produce error <nil>, instead Invalid character
    strings_test.go:193: expected <cyrillic nick>!Potato to canonicalize to <lowercased>!potato@*, instead
    strings_test.go:196: expected <cyrillic nick> to produce error <nil>, instead Invalid character
FAIL
FAIL	github.com/ergochat/ergo/irc	5.736s

(The literal non-ASCII nicks are elided here; they are Hebrew and Cyrillicnick cases gated on i18n.Enabled.) The failure is a test-isolation interaction, not a product-code regression: the new irc/s2s_test.go boots test servers with casemapping: ascii, which sets the package-global globalCasemappingSetting; since s2s_test.go sorts before strings_test.go", its tests run first and the global is still ASCII when TestCanonicalizeMaskWildcard's i18n-gated cases execute. Evidence: the test passes on pristine upstream master, and passes on this branch in isolation (go test ./irc/ -run TestCanonicalizeMaskWildcard`). Left unfixed deliberately — fixing it would mean editing the contributed test setup, which is the author's call.

Author credit: @waveplate.

waveplate and others added 7 commits September 19, 2026 22:15
- Implemented TS6 protocol handshake (PASS, CAPAB, SERVER, SVINFO, PING, PONG).
- Implemented network state bursting (SID, UID/EUID, SJOIN, BMASK, TB).
- Implemented routing, nickname/UID resolution, multi-hop broadcast, and TS collision handling.
- Implemented runtime propagation for JOIN, PART, KICK, MODE/TMODE, TOPIC, PRIVMSG/NOTICE, AWAY, and KILL.
- Implemented netsplit cleanup and routing graph updates.
- Added comprehensive unit and integration test suite in irc/s2s_test.go.
Waveplate's TS6 commits trip the local gofmt -s (struct literal alignment in irc/client.go, trailing blank lines in irc/s2s_link.go, irc/s2s_test.go, irc/server.go). Mechanical formatter output only; no hand edits.
@slingamn

Copy link
Copy Markdown
Member

Please see #1532 for previous discussion of this issue. The short answer is that TS6 and similar are not a good fit for Ergo, because core Ergo logic (e.g. nickname ownership) needs to be CP rather than AP.

At a more immediate level, I do not see how this PR addresses availability of the datastore (in particular the business logic in irc/accounts.go) across multiple linked nodes.

This branch has not been deployed

No deployments
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.

3 participants