Summary
Origin now refuses a delta whose Loro peer id is owned by another replica, with a terminal DeltaReject carrying PEER_ID_COLLISION and a Custom { constraint: "peer_id_collision" } compensation hint (NodeDB-Lab/nodedb#214). The refusal is loud and diagnosable, but no client acts on it.
A replica that hits this is permanently broken from that point: every write it pushes under the colliding peer id is refused, and nothing recovers unless a human reads the log and reinstalls the client.
Expected
On receiving a peer_id_collision refusal, the client should regenerate its Loro peer id for the affected collection and resync, rather than abandoning the write and continuing to push under an id the server will keep refusing.
Why this matters
The refusal replaced a silent-data-loss path — the merge used to discard those writes with a successful ack. Turning silent loss into loud, permanent failure is a strict improvement, but the recovery it implies is currently manual. Automatic recovery is the difference between "an operator eventually notices" and "the replica heals itself".
Notes
Two related cases the server deliberately cannot refuse, both of which the client is the only layer able to prevent:
- A client that wipes its local CRDT store but keeps its
lite_id and peer id collides with its own earlier history. The server owns no basis to reject it (the producer really does own that peer id), so those writes are reported Duplicate and dropped. The client should generate a new peer id whenever its local store is reset, reinstalled, or restored from another replica's backup.
- A session that connects without a
lite_id has no durable producer identity for a peer id to be bound to at all.
Summary
Origin now refuses a delta whose Loro peer id is owned by another replica, with a terminal
DeltaRejectcarryingPEER_ID_COLLISIONand aCustom { constraint: "peer_id_collision" }compensation hint (NodeDB-Lab/nodedb#214). The refusal is loud and diagnosable, but no client acts on it.A replica that hits this is permanently broken from that point: every write it pushes under the colliding peer id is refused, and nothing recovers unless a human reads the log and reinstalls the client.
Expected
On receiving a
peer_id_collisionrefusal, the client should regenerate its Loro peer id for the affected collection and resync, rather than abandoning the write and continuing to push under an id the server will keep refusing.Why this matters
The refusal replaced a silent-data-loss path — the merge used to discard those writes with a successful ack. Turning silent loss into loud, permanent failure is a strict improvement, but the recovery it implies is currently manual. Automatic recovery is the difference between "an operator eventually notices" and "the replica heals itself".
Notes
Two related cases the server deliberately cannot refuse, both of which the client is the only layer able to prevent:
lite_idand peer id collides with its own earlier history. The server owns no basis to reject it (the producer really does own that peer id), so those writes are reportedDuplicateand dropped. The client should generate a new peer id whenever its local store is reset, reinstalled, or restored from another replica's backup.lite_idhas no durable producer identity for a peer id to be bound to at all.