feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
2026-04-08 23:15:48 +02:00
|
|
|
use libchat::{
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
AddressedEnvelope, ChatError, ChatStorage, Context, ConversationId, ConvoOutcome,
|
2026-06-04 10:09:29 +08:00
|
|
|
DeliveryService, InboxOutcome, Introduction, PayloadOutcome, RegistrationService,
|
|
|
|
|
StorageConfig,
|
2026-04-08 23:15:48 +02:00
|
|
|
};
|
2026-03-24 18:21:00 -07:00
|
|
|
|
2026-05-20 13:18:25 -07:00
|
|
|
use components::EphemeralRegistry;
|
2026-05-19 11:54:54 -07:00
|
|
|
|
|
|
|
|
use crate::errors::ClientError;
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
use crate::event::Event;
|
2026-04-08 23:15:48 +02:00
|
|
|
|
2026-06-04 10:09:29 +08:00
|
|
|
pub struct ChatClient<D: DeliveryService, R: RegistrationService = EphemeralRegistry> {
|
|
|
|
|
ctx: Context<D, R, ChatStorage>,
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
|
2026-06-04 10:09:29 +08:00
|
|
|
// ── Default-registry constructors ────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
impl<D: DeliveryService + 'static> ChatClient<D, EphemeralRegistry> {
|
2026-04-08 23:15:48 +02:00
|
|
|
/// Create an in-memory, ephemeral client. Identity is lost on drop.
|
|
|
|
|
pub fn new(name: impl Into<String>, delivery: D) -> Self {
|
2026-05-19 11:54:54 -07:00
|
|
|
let registry = EphemeralRegistry::new();
|
2026-04-08 23:15:48 +02:00
|
|
|
let store = ChatStorage::in_memory();
|
2026-03-24 18:21:00 -07:00
|
|
|
Self {
|
2026-05-19 11:54:54 -07:00
|
|
|
ctx: Context::new_with_name(name, delivery, registry, store).unwrap(),
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-08 23:15:48 +02:00
|
|
|
/// Open or create a persistent client backed by `StorageConfig`.
|
|
|
|
|
///
|
|
|
|
|
/// If an identity already exists in storage it is loaded; otherwise a new
|
|
|
|
|
/// one is created and saved.
|
|
|
|
|
pub fn open(
|
|
|
|
|
name: impl Into<String>,
|
|
|
|
|
config: StorageConfig,
|
|
|
|
|
delivery: D,
|
|
|
|
|
) -> Result<Self, ClientError<D::Error>> {
|
|
|
|
|
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
2026-05-19 11:54:54 -07:00
|
|
|
let registry = EphemeralRegistry::new();
|
|
|
|
|
let ctx = Context::new_from_store(name, delivery, registry, store)?;
|
|
|
|
|
Ok(Self { ctx })
|
2026-04-08 23:15:48 +02:00
|
|
|
}
|
2026-06-04 10:09:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Caller-supplied registry + shared methods ────────────────────────────────
|
|
|
|
|
|
|
|
|
|
impl<D, R> ChatClient<D, R>
|
|
|
|
|
where
|
|
|
|
|
D: DeliveryService + 'static,
|
|
|
|
|
R: RegistrationService + 'static,
|
|
|
|
|
{
|
|
|
|
|
/// Open or create a persistent client with a caller-supplied registration
|
|
|
|
|
/// service. Use this to swap in a network-backed registry (e.g. the
|
|
|
|
|
/// testnet KeyPackage Registry) in place of the default in-memory store.
|
|
|
|
|
///
|
|
|
|
|
/// Submits this account's KeyPackage to the registry as the last step of
|
|
|
|
|
/// construction. The default in-memory `open` path skips this call, but
|
|
|
|
|
/// when a real registry is wired in we want each session to publish so
|
|
|
|
|
/// other clients can fetch it.
|
|
|
|
|
pub fn open_with_registry(
|
|
|
|
|
name: impl Into<String>,
|
|
|
|
|
config: StorageConfig,
|
|
|
|
|
delivery: D,
|
|
|
|
|
registry: R,
|
|
|
|
|
) -> Result<Self, ClientError<D::Error>> {
|
|
|
|
|
let store = ChatStorage::new(config).map_err(ChatError::from)?;
|
|
|
|
|
let mut ctx = Context::new_from_store(name, delivery, registry, store)?;
|
|
|
|
|
ctx.register_keypackage()?;
|
|
|
|
|
Ok(Self { ctx })
|
|
|
|
|
}
|
2026-04-08 23:15:48 +02:00
|
|
|
|
|
|
|
|
/// Returns the installation name (identity label) of this client.
|
|
|
|
|
pub fn installation_name(&self) -> &str {
|
|
|
|
|
self.ctx.installation_name()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Produce a serialised introduction bundle for sharing out-of-band.
|
|
|
|
|
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ClientError<D::Error>> {
|
|
|
|
|
self.ctx.create_intro_bundle().map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parse intro bundle bytes, initiate a private conversation, and deliver
|
|
|
|
|
/// all outbound envelopes. Returns this side's conversation ID.
|
|
|
|
|
pub fn create_conversation(
|
|
|
|
|
&mut self,
|
|
|
|
|
intro_bundle: &[u8],
|
|
|
|
|
initial_content: &[u8],
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
) -> Result<ConversationId, ClientError<D::Error>> {
|
2026-04-08 23:15:48 +02:00
|
|
|
let intro = Introduction::try_from(intro_bundle)?;
|
|
|
|
|
let (convo_id, envelopes) = self.ctx.create_private_convo(&intro, initial_content)?;
|
|
|
|
|
self.dispatch_all(envelopes)?;
|
|
|
|
|
Ok(convo_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// List all conversation IDs known to this client.
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
pub fn list_conversations(&self) -> Result<Vec<ConversationId>, ClientError<D::Error>> {
|
2026-04-08 23:15:48 +02:00
|
|
|
self.ctx.list_conversations().map_err(Into::into)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Encrypt `content` and dispatch all outbound envelopes.
|
|
|
|
|
pub fn send_message(
|
|
|
|
|
&mut self,
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
convo_id: &str,
|
2026-04-08 23:15:48 +02:00
|
|
|
content: &[u8],
|
|
|
|
|
) -> Result<(), ClientError<D::Error>> {
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
let envelopes = self.ctx.send_content(convo_id, content)?;
|
2026-04-08 23:15:48 +02:00
|
|
|
self.dispatch_all(envelopes)
|
|
|
|
|
}
|
|
|
|
|
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
/// Decrypt an inbound payload. Returns the events the payload produced,
|
|
|
|
|
/// in causal order. May be empty for protocol-only frames.
|
|
|
|
|
pub fn receive(&mut self, payload: &[u8]) -> Result<Vec<Event>, ClientError<D::Error>> {
|
|
|
|
|
let result = self.ctx.handle_payload(payload)?;
|
|
|
|
|
Ok(events_from_inbound(result))
|
2026-04-08 23:15:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn dispatch_all(
|
|
|
|
|
&mut self,
|
|
|
|
|
envelopes: Vec<AddressedEnvelope>,
|
|
|
|
|
) -> Result<(), ClientError<D::Error>> {
|
|
|
|
|
for env in envelopes {
|
2026-05-19 11:54:54 -07:00
|
|
|
let mut delivery = self.ctx.ds();
|
|
|
|
|
delivery.publish(env).map_err(ClientError::Delivery)?;
|
2026-04-08 23:15:48 +02:00
|
|
|
}
|
|
|
|
|
Ok(())
|
2026-03-24 18:21:00 -07:00
|
|
|
}
|
|
|
|
|
}
|
feat: introduce client event system (#106)
* chore(flake): accept extra system attr; add perl for openssl-sys build
forAllSystems calls the lambda with {system, pkgs}; strict
destructuring requires `..` to ignore the system attribute.
`pkgs.perl` is needed because openssl-sys is pulled vendored via
libsqlite3-sys / rusqlite / chat-sqlite, and its `perl Configure`
step needs FindBin.pm, which Fedora's system perl doesn't ship.
* feat: introduce client event system
- Core processing yields a `PayloadOutcome` enum — `Empty`, `Convo`, or
`Inbox`. `ConvoOutcome` carries a conversation id and an optional
decrypted `Content`; `InboxOutcome` adds a `NewConversation`
(id + `ConversationClass`) for a peer-initiated conversation.
- Client translates `PayloadOutcome` into app-facing `Vec<Event>`
(`ConversationStarted`, `MessageReceived`) at the boundary, so the
application loop sees discrete events rather than core types.
- MLS group welcomes produce a `ConversationStarted` event with no
initial content, fixing the silent-group-join case where the inbox
layer dropped the observation.
- C FFI exposes an `EventList` opaque type with indexed accessors and
an `Invalid` sentinel for out-of-bounds / non-applicable reads.
- Symmetric `Inbox` / `InboxV2` handlers: both return
`Result<InboxOutcome, _>` and own the persistence + ephemeral-key
cleanup for the conversations they create.
- Updated and simplified `docs/adr/0001-client-event-system.md`.
* chore(flake): bump nixpkgs to nixos-unstable-small
Temporary. The two crates.io UA fixes (NixOS/nixpkgs#512735 for
fetchCargoVendor's python-requests UA, NixOS/nixpkgs#524985 for
importCargoLock's curl UA) haven't propagated to nixos-unstable yet.
Switch to nixos-unstable-small and force logos-delivery to follow so
the smoketest gets the same fix. Revert once nixos-unstable catches up.
Refs:
- https://github.com/rust-lang/crates.io/issues/13482
- https://github.com/rust-lang/crates.io/issues/13783
- https://crates.io/data-access
2026-05-28 23:51:15 +02:00
|
|
|
|
|
|
|
|
/// Walk an [`PayloadOutcome`] in causal order and emit one `Event` per
|
|
|
|
|
/// observation. For an `Inbox` outcome, [`Event::ConversationStarted`]
|
|
|
|
|
/// precedes the message event. The convo id is wrapped into `Arc<str>` once
|
|
|
|
|
/// per outcome and shared across the events it produces.
|
|
|
|
|
fn events_from_inbound(result: PayloadOutcome) -> Vec<Event> {
|
|
|
|
|
match result {
|
|
|
|
|
PayloadOutcome::Empty => Vec::new(),
|
|
|
|
|
PayloadOutcome::Convo(co) => convo_events(co),
|
|
|
|
|
PayloadOutcome::Inbox(io) => inbox_events(io),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn convo_events(outcome: ConvoOutcome) -> Vec<Event> {
|
|
|
|
|
let ConvoOutcome { convo_id, content } = outcome;
|
|
|
|
|
content
|
|
|
|
|
.map(|c| Event::MessageReceived {
|
|
|
|
|
convo_id: Arc::from(convo_id),
|
|
|
|
|
content: c.bytes,
|
|
|
|
|
})
|
|
|
|
|
.into_iter()
|
|
|
|
|
.collect()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn inbox_events(outcome: InboxOutcome) -> Vec<Event> {
|
|
|
|
|
let InboxOutcome {
|
|
|
|
|
new_conversation,
|
|
|
|
|
initial,
|
|
|
|
|
} = outcome;
|
|
|
|
|
let id: Arc<str> = Arc::from(new_conversation.convo_id);
|
|
|
|
|
let mut events = Vec::with_capacity(2);
|
|
|
|
|
events.push(Event::ConversationStarted {
|
|
|
|
|
convo_id: Arc::clone(&id),
|
|
|
|
|
class: new_conversation.class,
|
|
|
|
|
});
|
|
|
|
|
if let Some(c) = initial.and_then(|co| co.content) {
|
|
|
|
|
events.push(Event::MessageReceived {
|
|
|
|
|
convo_id: Arc::clone(&id),
|
|
|
|
|
content: c.bytes,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
events
|
|
|
|
|
}
|