fix: signer-scoped DirectV1 routing (#162)

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 commit is contained in:
osmaczko
2026-07-03 23:18:10 +02:00
committed by GitHub
parent d131a69583
commit c09459c0a0
34 changed files with 692 additions and 500 deletions
+7 -9
View File
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use anyhow::Result;
use arboard::Clipboard;
use crossbeam_channel::Receiver;
use logos_chat::{ChatClient, ChatStore, Event, IdentityProvider, RegistrationService, Transport};
use logos_chat::{AccountDirectory, ChatClient, ChatStore, Event, RegistrationService, Transport};
use serde::{Deserialize, Serialize};
use crate::utils::now;
@@ -41,14 +41,13 @@ pub struct AppState {
pub active_chat: Option<String>,
}
pub struct ChatApp<I, T, R, S>
pub struct ChatApp<T, R, S>
where
I: IdentityProvider + Send + 'static,
T: Transport,
R: RegistrationService + Send + 'static,
R: RegistrationService + AccountDirectory + Clone + Send + 'static,
S: ChatStore + Send + 'static,
{
pub client: ChatClient<I, T, R, S>,
pub client: ChatClient<T, R, S>,
events: Receiver<Event>,
pub state: AppState,
/// Ephemeral command output — not persisted, cleared on chat switch.
@@ -59,15 +58,14 @@ where
state_path: PathBuf,
}
impl<I, T, R, S> ChatApp<I, T, R, S>
impl<T, R, S> ChatApp<T, R, S>
where
I: IdentityProvider + Send,
T: Transport,
R: RegistrationService + Send + 'static,
R: RegistrationService + AccountDirectory + Clone + Send + 'static,
S: ChatStore + Send,
{
pub fn new(
client: ChatClient<I, T, R, S>,
client: ChatClient<T, R, S>,
events: Receiver<Event>,
user_name: &str,
data_dir: &Path,