fix(call-to-play): acknowledge live replication

Raise the wire protocol to version 7 and add explicit Call to Play delivery
outcomes. Live requests now wait for an application acknowledgement, allowing
the sender to distinguish applied, duplicate, obsolete, incomplete, and
rejected updates instead of treating a successful write as acceptance.

Remove source-IP equality from actor verification. The receiver now requires
the envelope peer ID to be present in its known roster and requires every live
event actor to match that envelope. This matches the cooperative-LAN trust
model without misrepresenting the shared TLS identity as per-peer
authentication.

Transport failures, malformed responses, NeedHandshake, and NeedHistory each
trigger one asynchronous Hello/HelloAck resync. Rejections are logged without
retry, and local publication remains independent of remote availability.

Test Plan:
- `just fmt` -- passed
- `just clippy` -- passed
- `just test` -- passed
- `git diff --cached --check` -- passed
This commit is contained in:
2026-07-23 17:49:01 +02:00
parent be7ad2e560
commit e5d70ae56f
5 changed files with 254 additions and 57 deletions
+14 -9
View File
@@ -63,17 +63,22 @@ local action is applied to that history, sent to the UI, and broadcast to every
currently known peer. An incoming live event is applied once and sent to the UI
without being rebroadcast, which prevents forwarding loops.
If a live Call to Play delivery fails, the sender immediately falls back to a
normal `Hello` / `HelloAck` exchange with that peer. The handshake carries the
full active history in both directions, so a transient request failure heals
without waiting for mDNS rediscovery or a later reconnect.
Live Call to Play delivery is acknowledged by the receiver. Applied, duplicate,
and obsolete events need no follow-up. An unknown envelope peer, missing call
root, transport failure, or malformed acknowledgement makes the sender perform
one normal `Hello` / `HelloAck` exchange with that peer. The handshake carries
the full retained history in both directions, so a transient request failure
heals without waiting for mDNS rediscovery or a later reconnect. A rejected
event is logged without retry. Local publication remains successful while this
healing happens asynchronously, so an offline peer cannot block an action.
Actors are keyed by the peer's stable ID and carry a separate display name. The
origin peer overwrites the actor ID on local actions, and live-event envelopes
must match the known sending peer. This prevents duplicate default usernames
from merging participants and protects creator controls from other normal
clients. It is not authentication against a hostile LAN peer; the QUIC setup
uses the project's trusted-LAN identity model.
origin peer overwrites the actor ID on local actions. A live-event envelope must
name a peer already in the receiver's roster, and every enclosed actor ID must
match that envelope. This prevents accidental identity mixing and protects
creator controls from other normal clients. It is not authentication against a
hostile LAN peer: all peers use the shared application TLS identity, and stable
peer IDs are self-asserted under the project's trusted-LAN model.
`Hello` and `HelloAck` include each side's event history. This lets peers that
join after a call was created reconstruct the same nominations, responses,