osmaczko 7332c152c0
feat: durable MLS storage folded into a single ChatStore
Implements issue #112: MLS group state must survive process restarts. The MLS
provider hardcoded an in-memory store separate from the chat store, so a restart
lost every group. Fold the OpenMLS StorageProvider into libchat's own ChatStore
so one durable store holds both chat state and MLS group state.

- storage: ChatStore subsumes StorageProvider<CURRENT_VERSION>, so a ChatStore is
  the whole durable-storage contract (identity/ephemeral/conversation/ratchet
  sub-stores plus the MLS key-value surface).
- chat-sqlite: ChatStorage implements StorageProvider over an mls_kv table
  (migration 003_mls_storage), a byte-faithful port of MemoryStorage with decode
  errors surfaced instead of unwrapped and the reference clear_proposal_queue
  orphan-key bug fixed. The separate SqliteMlsStorage type is removed.
- conversations: MlsPqProvider becomes a transient view borrowing the shared
  store and a long-lived crypto backend, so the store stays singly owned;
  ServiceContext hands it out via mls_provider(). ExternalServices drops its ST
  associated type, leaving one CS. GroupV2/de_mls's thread-safe-error bound is
  restated on CS because an associated-type bound does not elaborate through a
  supertrait.
- client: the store is always ChatStorage, so fix it as such and drop the
  vestigial store generic from ChatClient and the builder. Retire the stubbed
  in-memory MemStore in favour of ChatStorage::in_memory().
- GroupV1 conversations resume across a restart through the existing
  MlsGroup::load path, proven by a drop-and-reload integration test.
2026-07-01 19:55:02 +02:00
..
2026-06-26 10:05:28 -07:00

chat-cli

A terminal chat application built on top of libchat. End-to-end encrypted messaging in your terminal.

Building

logos-delivery is exposed as a Nix package. Build it once, then point LOGOS_DELIVERY_LIB_DIR at the result:

nix build .#logos-delivery
LOGOS_DELIVERY_LIB_DIR=./result/lib cargo build --release -p chat-cli

The binary lands at target/release/chat-cli.

Transports

Both transports are compiled into the binary and selected at runtime via --transport:

Value (--transport) Description
logos-delivery (default) Embedded Waku node on the logos.dev network
file Shared directory; no network needed — great for local testing

Quick start

Run two instances in separate terminals:

# Terminal 1
cargo run -p chat-cli -- --name alice --port 60001

# Terminal 2
cargo run -p chat-cli -- --name bob --port 60002

For local-only testing without any network dependency, use the file transport:

# Terminal 1
cargo run -p chat-cli -- --name alice --transport file

# Terminal 2
cargo run -p chat-cli -- --name bob --transport file

Establishing a connection

  1. In Alice's terminal, type /intro — the bundle is copied to your clipboard automatically.
  2. In Bob's terminal, type /connect <paste bundle here>.
  3. Bob's "Hello!" message appears in Alice's terminal. Both can now chat.

Optional: KeyPackage registry

When --registry-url <url> is set, the client publishes its MLS KeyPackage to the keypackage-registry service on startup so other clients can later fetch it by account_id. Without the flag, an in-memory registry is used and is only visible inside the local process.

# Terminal 1 — registry server (from a chat-store checkout)
cargo run -- --bind 127.0.0.1:18080

# Terminal 2 / 3 — chat clients pointing at it
cargo run -p chat-cli -- --name alice --transport file \
  --registry-url http://127.0.0.1:18080
cargo run -p chat-cli -- --name bob --transport file \
  --registry-url http://127.0.0.1:18080

The registry is a throwaway testnet helper; v0.3 replaces it with a λLEZ-based discovery service.

Options

Flag Default Description
--transport <kind> logos-delivery Transport to use (logos-delivery or file)
--data <dir> tmp/chat-cli-data Data directory (UI state and default SQLite path)
--db <path> <data>/<name>.db SQLite file for persistent identity
--preset <name> logos.dev logos-delivery network preset
--port <n> 60000 TCP port for the embedded logos-delivery node
--registry-url <url> (unset) Use the HTTP-backed keypackage-registry at this URL instead of the in-memory registry
--log-file <path> (stderr, off) Write logs to a file instead of stderr

Commands

Command Description
/help Show available commands
/intro Generate your introduction bundle (copies to clipboard)
/connect <bundle> Connect to a user using their introduction bundle
/chats List all established chats
/switch <user> Switch active chat
/delete <user> Delete a chat session
/status Show identity and connection info
/clear Clear current chat's message history
/quit · Esc · Ctrl+C Exit

Storage

All data lives under tmp/chat-cli-data/ by default (override with --data):

Path Contents
<name>.db SQLite — identity keys, ratchet state, chat metadata (encrypted)
<name>_state.json UI state — message history, active chat
transport/<name>/ Inbox directory watched for incoming messages (file transport only)

The SQLite database can be inspected with DB Browser for SQLite: password chat-cli, cipher SQLCipher 4 defaults.

Architecture

bin/chat-cli/
├── src/
│   ├── main.rs           entry point, CLI arg parsing, runtime transport dispatch
│   ├── app.rs            application state and command handling
│   ├── ui.rs             ratatui terminal UI
│   ├── utils.rs          shared helpers
│   ├── transport.rs      module declarations
│   └── transport/
│       ├── file.rs       file-based transport
│       └── logos_delivery.rs   logos-delivery (Waku) transport + FFI
└── build.rs              links liblogosdelivery (LOGOS_DELIVERY_LIB_DIR required)