fix: subscribe InboxV2 on routing_id, not the credential id#155
Closed
osmaczko wants to merge 1 commit into
Closed
fix: subscribe InboxV2 on routing_id, not the credential id#155osmaczko wants to merge 1 commit into
osmaczko wants to merge 1 commit into
Conversation
9fb2ae7 to
75435b3
Compare
InboxV2 gated inbound on the credential id (hex of the DelegateCredential TLV), but a sender addresses the Welcome to the invitee's routing address, which the account directory resolves to the device key HttpRegistry stores the key-package under. The two differ, so the Welcome fell through to PayloadOutcome::Empty and the invitee never joined. EphemeralRegistry hid this by keying key-packages on the credential id. This is routing-vs-credential, not account-vs-device: the TLV-wrapped credential differs from the address even when account key == device key (testnet today), so a client associating over HttpRegistry hits it; only the in-process EphemeralRegistry path is unaffected. Add IdentityProvider::routing_id() (defaults to id()); DelegateSigner derives it from its credential (the account address once associated, else id()); Core::assemble subscribes InboxV2 on routing_id(). id() still backs the MLS credential, member id, sender id, and decode_sender, so membership and attribution are unchanged. Regression test direct_v1_welcome_routes_on_routing_id over a device-key-keyed registry (as HttpRegistry) fails without the fix.
75435b3 to
2321e4c
Compare
Contributor
Author
|
Superseded per the team decision that core should not be account-aware. This PR aligned the Welcome-routing mismatch by moving the inbox subscription up to the account address; #162 moves the sender side down to signer scope instead: accounts resolve to signer ids in the client layer, and one byte-string (the hex of the signer's verifying key) serves as registry key, inbox subscription, and Welcome routing target end to end. |
osmaczko
added a commit
that referenced
this pull request
Jul 2, 2026
Core is no longer account-aware: conversations take signer (installation) ids, and resolving an account address to its signers happens in the client layer. A DirectV1 Welcome over the deployed HttpRegistry could not reach the invitee: the sender addressed the account id it was given while the invitee's InboxV2 listened on its credential id, and neither matches the device id the registry keys key packages under. EphemeralRegistry masked all of it by keying on the credential id. One byte-string now plays the rendezvous role end to end: the signer routing id, the hex of the signer's verifying key. It is what the account directory bundle lists, what the registries key key packages under, what InboxV2 subscribes on, what a Welcome is addressed to, and what the causal-history sender hint carries. The MLS credential stays the full id() so membership and attribution are unchanged. - client: signers_from_account resolves the account through the directory (resolve_device_ids); an address with no published bundle is treated as a signer id, so unassociated peers stay reachable. ChatClient::addr() is the signer routing address; an application with an account shares the account address instead. - core: InboxV2 subscribes on hex(public_key()); GroupV1 fetches one key package per signer (key_package_for_signer) instead of resolving accounts. - GroupV2: de-mls matches members by their MLS leaf credential, so the member id is read from the fetched key package instead of assumed equal to the address; pending_invites maps it to the signer routing id the welcome is then delivered to. - core: drop the account-bundle auto-publish (new_with_name) and InboxV2::publish_device_bundle; MlsIdentityProvider's testnet AccountAuthority shim goes with it. The lamport-upsert publish moves to the client layer as logos_chat::publish_device_bundle(directory, authority, device): custody stays injected (AccountAuthority is only asked to sign), and TestLogosAccount implements AccountAuthority so tests and dev flows sign bundles through the proper trait. - EphemeralRegistry keys key packages by device id like HttpRegistry, so tests exercise the deployed keying. - test direct_v1_by_account_address: a peer is reachable by its account address alone over a device-key-keyed registry. Supersedes #155 (routing_id), which aligned the same mismatch in the opposite direction (account-scoped inbox). Per team discussion (2026-07-02) the core should not be aware of accounts.
osmaczko
added a commit
that referenced
this pull request
Jul 2, 2026
Core is no longer account-aware: conversations take signer (installation) ids, and resolving an account address to its signers happens in the client layer. A DirectV1 Welcome over the deployed HttpRegistry could not reach the invitee: the sender addressed the account id it was given while the invitee's InboxV2 listened on its credential id, and neither matches the device id the registry keys key packages under. EphemeralRegistry masked all of it by keying on the credential id. One byte-string now plays the rendezvous role end to end: the signer routing id, the hex of the signer's verifying key. It is what the account directory bundle lists, what the registries key key packages under, what InboxV2 subscribes on, what a Welcome is addressed to, and what the causal-history sender hint carries. The MLS credential stays the full id() so membership and attribution are unchanged. - client: signers_from_account resolves the account through the directory (resolve_device_ids); an address with no published bundle is treated as a signer id, so unassociated peers stay reachable. ChatClient::addr() is the signer routing address; an application with an account shares the account address instead. - core: InboxV2 subscribes on hex(public_key()); GroupV1 fetches one key package per signer (key_package_for_signer) instead of resolving accounts. - GroupV2: de-mls matches members by their MLS leaf credential, so the member id is read from the fetched key package instead of assumed equal to the address; pending_invites maps it to the signer routing id the welcome is then delivered to. - core: drop the account-bundle auto-publish (new_with_name) and InboxV2::publish_device_bundle; MlsIdentityProvider's testnet AccountAuthority shim goes with it. The lamport-upsert publish moves to the client layer as logos_chat::publish_device_bundle(directory, authority, device): custody stays injected (AccountAuthority is only asked to sign), and TestLogosAccount implements AccountAuthority so tests and dev flows sign bundles through the proper trait. - EphemeralRegistry keys key packages by device id like HttpRegistry, so tests exercise the deployed keying. - test direct_v1_by_account_address: a peer is reachable by its account address alone over a device-key-keyed registry. Supersedes #155 (routing_id), which aligned the same mismatch in the opposite direction (account-scoped inbox). Per team discussion (2026-07-02) the core should not be aware of accounts.
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - AccountAuthority is narrowed to attest_devices (typed device-set attestation); directory-side add_device owns the lamport upsert. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_device does the lamport upsert and signs internally. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_device does the lamport upsert and signs internally. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_signer does the lamport upsert and signs internally. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_delegate_signer does the lamport upsert and signs internally. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_delegate_signer does the lamport upsert and signs internally. - Every client acts for an account (ChatClientBuilder::new(account)). DelegateSigner is a pure keypair; the client composes the wire credential from the signer and the account, so the association is client state. addr() is the account address. - resolve_device_ids fails fast (NotAnAccountKey / NoDeviceBundle / Directory) instead of falling back to treating an unresolved address as a signer id. LogosChatClient::open and chat-cli mint and publish a dev account each launch. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
osmaczko
added a commit
that referenced
this pull request
Jul 3, 2026
Core is no longer account-aware: the client resolves an account address to signer ids via the account directory, and the signer's verifying-key hex serves as registry key, inbox subscription, and Welcome routing target end to end. The MLS credential stays the full id(). - GroupV2 reads the de-mls member id from the fetched key package and maps it to the signer id the welcome is delivered to. - All account machinery (directory trait, bundle codec, resolution) moves out of core into logos-account; the RegistrationService supertrait and Core::account_directory() are gone, and the client holds its own directory handle. - The account exposes functionality, never a signer: add_delegate_signer does the lamport upsert and signs internally. - Every client acts for an account (ChatClientBuilder::new(account)). DelegateSigner is a pure keypair; the client composes the wire credential from the signer and the account, so the association is client state. addr() is the account address. - resolve_device_ids fails fast (NotAnAccountKey / NoDeviceBundle / Directory) instead of falling back to treating an unresolved address as a signer id. LogosChatClient::open and chat-cli mint and publish a dev account each launch. - EphemeralRegistry keys key packages by hex pubkey like HttpRegistry. Supersedes #155 (routing_id).
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.
InboxV2 gated inbound on the credential id (hex of the DelegateCredential TLV),
but a sender addresses the Welcome to the invitee's routing address, which the
account directory resolves to the device key HttpRegistry stores the key-package
under. The two differ, so the Welcome fell through to PayloadOutcome::Empty and
the invitee never joined. EphemeralRegistry hid this by keying key-packages on
the credential id.
This is routing-vs-credential, not account-vs-device: the TLV-wrapped credential
differs from the address even when account key == device key (testnet today), so
a client associating over HttpRegistry hits it; only the in-process
EphemeralRegistry path is unaffected.
Add IdentityProvider::routing_id() (defaults to id()); DelegateSigner derives it
from its credential (the account address once associated, else id());
Core::assemble subscribes InboxV2 on routing_id(). id() still backs the MLS
credential, member id, sender id, and decode_sender, so membership and
attribution are unchanged. Regression test direct_v1_welcome_routes_on_routing_id
over a device-key-keyed registry (as HttpRegistry) fails without the fix.