Defaulting

How a friendship is disabled when someone can't (or won't) settle, how it can come back, and how one side can drop it for good.


1. Why

Someone who can't pay back is better off telling their friend than quietly abandoning Tally. Defaulting does that openly, with an optional note. The same step absolves a friend's debt, deactivates a zero-tally friendship, and removes test or mistaken friends from the list.

2. The two steps

2.1 Default — the friendship is disabled

The friend page's danger action sends a signed friendship_default message. Its payload holds the note and the tally at that moment, from the author's side. The button is labelled Default on tally, Absolve tally or Deactivate friend connection, depending on which way the tally points. Both sides then set friendship_status to disabled (glossary):

2.2 Delete history — final for this user

On a disabled friend's page, Delete history removes the friend record, contact, queued messages and every ledger entry between the two. A signed, self-addressed friendship_history_deleted entry records the deletion. It is never sent to the friend. Self-sync carries it to the user's other devices, which delete their copies too (bridge.js). Until the friend shows up again, any entry with them dated before the deletion is dropped wherever it comes from, whether self-sync, a replay or the friend (isDeletedHistoryEntry in ledger.ts), so the history can't creep back.

After this the user cannot restore the friendship. The friend still can, if they kept their copy.

3. Reactivation

Either side of a disabled friendship can suggest reactivation. The reactivation_request carries the requester's copy of the shared history, because the recipient may have deleted theirs. Earlier requests are left out of it, so histories never nest.

The outer signature only proves who sent the list. So every history entry is checked on its own (verifyReactivationHistory):

One bad entry rejects the whole suggestion, since a partial history would give a wrong tally. If the recipient had deleted the history, the friend comes back as disabled, with the tally rebuilt from the verified records and an incoming suggestion to Reactivate or Decline.

A reactivation_response answers it. On acceptance both sides return to accepted, and the records count again. A suggestion is open until a response with its request_id is in the ledger (getOpenReactivationRequests), like payment requests. Suggesting while the friend's own suggestion is open counts as accepting it.

A disabled friend's fresh friend_request, or an old friend_accept replayed by sync, is ignored. Reactivation is the only way back, so the old tally never revives unannounced.

4. Tests

tests/two-client-e2e/scenarios/friendship-default.cjs walks the whole flow through the UI. It covers the default with a note, the disabled views, history deletion, a suggestion with a tampered record being rejected, the real suggestion restoring the deleted side, and acceptance. tests/ledger-validation covers the new types' value rules.