Repository navigation
Conversation
… Welcome Accepting a message request fetched the sender's bundle from the address the Welcome names and imported it as an Available contact before anything was authenticated: only the bundle's user id was compared with the user the Welcome's credential claims, and that credential is a string the Welcome's maker chooses. Join checked the signer later, but by then the contact was stored. So a Welcome naming someone, with a reference to their real bundle, left them in the receiver's contacts, and the receiver could then start a conversation with them although they never shared a link. Staging a Welcome already verifies its GroupInfo signature under the author's leaf key. inspect_welcome now returns that key, the preview carries it, and both accept flows (CLI and desktop) require it to equal the device key the fetched bundle names before importing anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A rebuild after a lost race (and a rebuild the user confirms) enqueued the commit that created the new group ahead of its Welcome. Everyone in that group joins from the Welcome, so the commit had no reader: it went out under the new session's commit key for epoch 1 with an arbitration signature over base 0, sat in the winner's quarantine until the join, and was then dropped as superseded. A first contact already sends only its Welcome. The emission ledger now also says why the winner sends ContactAccepted after joining a rebuild: it is the first record of the new session the loser processes, and that is what ends the loser's recovery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four fixes found while writing the spec of the client (proof/spec, §4.8),
and one found on the way:
- The KeyPackage pool count was requested at /v1/keypackage-pool/{dev}/count.
The host serves it at /v1/keypackage-pool/{dev} and has no /count route, so
every maintenance run was refused and the pool was never topped up: claims
fell back to the last-resort package. The request now goes to the host's
route.
- A move carried the sync cursor to the new host, which numbers its records
from 1: nothing was fetched there until its head passed the old ack, and
acks above its head were refused. A move now starts the new host's cursor
afresh, drops the pending ack, and checks the new host's empty pool.
- A lane notice sent in the epoch a race took was never opened, and the next
refresh that repeats it may be days away; after a move the peer kept
writing to the host left behind. The loser now announces its lane after its
rebuild, and the winner after joining it.
- A failed lane registration was not retried, so every later append on that
lane became a message request. A retryable failure is now retried, at the
slowest pace, while the lane is still one this device reads.
- Pending commits, the frames kept with them and fork witnesses expired only
on a send or a receipt, so a quiet or compromised session kept their keys
and KeyPackage secrets indefinitely. The daily upkeep now expires them too.
Each change has a test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
koabula
enabled auto-merge (squash)
November 11, 2026 08:53
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes found while writing the specification of the 1:1 client (Π_T) for the security proof. Three commits; each change has a test.
1. A requester is imported only once its device key signed the Welcome (c4603b6)
Accepting a message request fetched the sender's bundle from the address the Welcome names and imported it as an Available contact. The only check was that the bundle's user id equals the user the Welcome's credential claims, and that credential is a string the Welcome's maker chooses. Join checked the signer later, but by then the contact was stored. So a Welcome naming someone, with a reference to their real bundle, left them in the receiver's contacts, and the receiver could then start a conversation with them although they never shared a link.
Staging a Welcome already verifies its GroupInfo signature under the author's leaf key.
inspect_welcomenow returns that key and the preview carries it. Both accept flows (CLI and desktop) require it to equal the device key in the fetched bundle before importing anything.2. A rebuild sends only its Welcome (e140569)
After a lost race, or a rebuild the user confirms, the commit that created the new group was sent ahead of the Welcome. Nobody can read it: everyone in the group joins from the Welcome. It went out under the new session's commit key for epoch 1, with an arbitration signature over base 0, and the peer quarantined it and then dropped it as superseded. A first contact already sends only its Welcome.
The emission ledger now also says why the winner sends ContactAccepted after joining a rebuild: it is the first record of the new session the loser processes, and that ends the loser's recovery.
3. Moves, races and quiet sessions (1c3f290)
/v1/keypackage-pool/{dev}/count. The host serves it at/v1/keypackage-pool/{dev}and has no/countroute, so every maintenance run was refused and the pool was never topped up. Claims fell back to the last-resort package.Not changed here
Not verified locally
There is no Rust toolchain on this machine; CI is the first build.
🤖 Generated with Claude Code