Skip to content

fix: a member added live is kept with no handshake, and apply says what ran (keel#117, keel#120) - #123

Merged
marcos-mendez merged 1 commit into
mainfrom
fix/peer-added-live
Oct 9, 2026
Merged

marcos-mendez merged 1 commit into
mainfrom
fix/peer-added-live

Conversation

@marcos-mendez

@marcos-mendez marcos-mendez commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #117 and #120. Evidence: Keel-Linux/handbook#62, findings N1, N4 and N5. Decision on #117: the coordinator's, under the maintainer's delegation. A peer-only addition that is applied live is kept with no handshake. Removals, [Interface] changes and bounces keep the window. A peer with no handshake is drift.

#117: root cause

A new peer was kept only after a handshake within 120 s. On a mesh whose nodes had already joined, each side of a missing pair added the other at a different time. The other side did not hold this node yet, so no handshake came. Both sides reverted and then left the other out for one hour (unreached.json), at different times, so they never met.

#117: change

  • keel/network/switch.py: live_plan says whether the wg set lines only add peers that the interface does not hold. live_change returns ADDED or CHANGED. changed writes added_live in the window's marker (keel/network/marker.py).
  • keel/network/session.py, keel/network/confirm.py: new origin ADDED. It confirms only a change marked added_live, after the route check, and is refused for any other change.
  • keel/mesh/sync.py (kept_live): a change that only added members live is kept at once. Each new member is sent one packet, and the sync says "member is kept with no handshake yet". Every other change still waits for a handshake in its window.
  • keel/mesh/status.py: a drift: line for each peer with no handshake, or a peer that the interface does not hold.
  • keel/diff/handshakes.py (new), keel/diff/observe.py: network.overlay.wireguard.peers.<key>.handshake. It is drift for no handshake since the interface came up, or for a peer that the interface does not hold. It is same, with the age of the last handshake, for the others. Live system only.
  • Docs: docs/mesh.md, docs/diff.md, docs/apply.md.

#120 (N4, N5)

  • keel/system/execute.py, keel/system/effects.py (Effects.ran): the apply line says what ran. A database certificate that is not due prints "kept: the certificate is not due for renewal, and no file changed". An overlay change prints "done with wg set, live: ..." or "done with wg-quick down, then up". The plan line (keel/system/actions.py) no longer names a way.

Version 0.23.11 (main is at 0.23.10).

Tests

  • Unit: marker, origin and its refusal, classification, sync (kept live, refused by the route check, a change that did more, a repair kept live), keel diff, keel mesh status, apply lines.
  • tests/test_mesh_late_netns.py (new, clean and poor links, in "mesh / trixie", job timeout 30 min): B adds C live, and C adds B 180 s later. Both keep the other, no change reverts, and they do a handshake and ping each other. On 0.23.7: B reverted and dropped C.

CI

Rebased on main after #128 (keel#94). The netns drivers use main's ping(..., upto_s=...) for a member kept live with no handshake yet. The earlier runs on 17e2ad4 failed mariadb / trixie on the poor-link crash case (attempt 1 and its one rerun); that flake is the subject of keel#94.

All 23 checks pass on head 98d1dc7, with no rerun.

@marcos-mendez
marcos-mendez force-pushed the fix/peer-added-live branch 4 times, most recently from 98d8dbe to 7974c83 Compare October 9, 2026 17:42
…at ran (keel#117, keel#120)

keel#117. On real nodes with keel 0.23.7 (handbook PR #62, finding N1),
db-1 and db-2 added web-2 and web-3, and web-2 and web-3 added db-1 and
db-2, but each side at a different time. A new peer was kept only after
a handshake within 120 s, and the other side did not hold this node
yet, so no handshake came. Both sides reverted and left the other out
for an hour, at different times, so they never met.

A change that only adds peers with wg set on an interface that is up
removes nothing and restarts nothing, so it cannot cut this node off.
keel.network.switch now says so in the window's marker (added_live),
and keel mesh sync keeps such a change at once with the new origin
session.ADDED: the route check still runs, and confirm refuses that
origin for any other change. Each new member is sent one packet, and
the sync says "member <key> is kept with no handshake yet". A change
that removes a peer, changes [Interface] or bounces the interface still
waits for a handshake in its window.

A peer with no handshake is drift: keel mesh status names it on a drift
line, and keel diff reports network.overlay.wireguard.peers.<key>.handshake
(drift for no handshake since the interface came up, or a peer the
interface does not hold; same, with the age of the last handshake, for
the others), on the live system only.

keel#120 (N4, N5). keel spec apply says what ran: a database
certificate that is not due is "kept: the certificate is not due for
renewal, and no file changed", not "done"; an overlay change is "done
with wg set, live" or "done with wg-quick down, then up", and the plan
line names no way.

Tests: unit tests for the marker, the origin and its refusal, the
classification, the sync (kept live, refused by the route check, a
change that did more), keel diff and keel mesh status, and the apply
lines. In network namespaces (tests/test_mesh_late_netns.py, clean and
poor links, in "mesh / trixie"): B adds C live, and C adds B 180 s
later. Both keep the other, no change reverts, and they complete a
handshake and ping each other. On 0.23.7, B reverted and dropped C.
Since a sync no longer waits for the handshake, the netns drivers ping
a new member for up to ten rounds, which stop at the first answer
(keel#94).

Version 0.23.9.
@marcos-mendez
marcos-mendez merged commit acc44f4 into main Oct 9, 2026
30 checks passed
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.

a peer added on both sides at different times never confirms: both revert and wait 1 h

1 participant