osmaczko e5bf820863 ci: cache Nix builds in Attic
Nothing here populates a binary cache. The Cachix step has never had a token, so all four workflows rebuild the same app closure from source on every push, four times over, and the doc-test job spends more than twenty minutes on it before the first spec runs.

Push to the Logos Attic cache from every build: master jobs enter the public-cache environment and push to the public cache, everything else uses the repo-wide token and the ci cache. That step self-skips until this repo is granted Attic credentials, and on fork PRs, which receive no secrets; `attic use` fails on an empty endpoint.

Both caches serve reads anonymously, so the substituter goes in the Nix install rather than behind those credentials. Cachix stays on as a read-only substituter, since Attic does not yet carry the Rust crate closure.
2026-07-23 00:16:27 +02:00
2026-01-28 18:34:47 +02:00
2026-02-17 21:30:39 +00:00
2026-02-17 21:30:39 +00:00

logos-chat-ui

A QML + C++ backend UI module for the Logos platform that provides a private messaging interface built on top of Logos Chat.

The UI connects to logos-chat-module via the Logos Core module system for all chat operations — identity, conversations, and message exchange happen over the Logos network.

Built with logos-module-builder using the mkLogosQmlModule pattern (QML frontend + C++ backend with Qt Remote Objects).

What It Does

The application provides a dark-themed chat interface with a conversation list (left), a message thread (center), and a members panel (right) that appears only for group conversations:

  • Conversation list (left panel) — active conversations with timestamps and unread indicators; group rows are prefixed with #
  • Message thread (center panel) — messages and a text input for the selected conversation; group messages show a short sender label above incoming bubbles
  • Members panel (right panel, groups only) — the group's roster, with an add-member field

Core functionality:

  • Identity — on startup, initializes a chat identity and displays the user's ID in the status bar
  • Addresses — show your address (the Show My Address button) and share it with others to let them start a conversation with you
  • Direct messages — paste another user's address into New DM to open a private (1:1) conversation
  • Group conversations — start a group with New group, then invite peers by address from the members panel (see below)
  • Messaging — send and receive messages in real-time over the Logos network
  • Chat lifecycle — auto-initializes and starts on launch; status shown in the bottom bar

Conversations are ephemeral — messages and identity exist only while the app is running.

Group conversations

  • Group creates a group with you as its only member (no dialog; the group opens immediately).
  • Collect peers' addresses (each shares theirs via Show My Address), paste one into the members panel's add-member field, and press Add to invite.
  • Membership changes are asynchronous: on devnet the group's steward commits an add only after a ~60s commit-inactivity window, then the welcome is delivered, so a peer joins minutes after the invite. The roster refreshes on selection, a message from a new member, the reload-roster button, or your own add.
  • Any member can add another; the invite routes from whoever proposed it.
  • During the brief windows while the group is finalizing a membership change, de-mls rejects sends; these surface as an error toast, so retry after a moment.

How to Run

# Run directly
nix run

# With local workspace overrides (if testing local changes)
nix run --override-input chat_module path:../logos-chat-module \
        --override-input chat_module/logos-module-builder path:../logos-module-builder

The standalone app starts Logos Core, loads capability_module and chat_module, then launches the QML UI via an isolated ui-host process.

Running multiple instances on one machine

To try a real conversation or group locally, run two or more standalone apps side by side on the same host. Each instance needs its own session directory; the UI-to-backend QtRO socket name is randomized per instance and the delivery node listens on ports it picks itself, so nothing else has to be set:

# window A
nix run . -- --user-dir ~/.local/share/chat_a
# window B
nix run . -- --user-dir ~/.local/share/chat_b

Add further windows the same way, giving each a fresh session directory (chat_c, and so on).

The standalone app hands every module its own directory under <session dir>/module_data, so --user-dir is what keeps two instances' chat state apart; it defaults to the platform application data location.

Variable Purpose
LOGOS_USER_DIR The standalone app's session directory, for when setting it by environment is easier than by flag. --user-dir wins over it.
QML_INSPECTOR_PORT Only needed when attaching the logos-qt-mcp inspector to drive an instance programmatically (default 3768); give each a distinct one then. Interactive use does not need it.

Each node joins the logos.test Waku fleet and publishes its key package during init, so this needs internet and ~5-20s per window to reach Online. Then share one window's address (Show My Address) and paste it into another (New DM for a 1:1, or New group then the members panel for a group). For the full walkthrough with screenshots, and the scripted drivers that automate it (doctests/exchange/run-exchange.sh for a two-party exchange, doctests/group/run-group.sh for a three-party group), see Two-instance message exchange.

In Basecamp

Build the .lgx package and install it:

# Build LGX
nix build .#lgx

# Install into Basecamp's plugin directory
lgpm --ui-plugins-dir ~/Library/Application\ Support/Logos/LogosBasecampDev/plugins \
     install --file result/*.lgx

Or from the workspace:

ws bundle logos-chat-ui --auto-local

Build Targets

nix build            # default — combined plugin + QML output
nix build .#lgx      # .lgx package for distribution
nix build .#install  # lgpm-installed output (modules/ + plugins/)
nix run              # standalone app with chat_module
nix develop          # enter development shell

Documentation

  • Two-instance message exchange — two windows exchanging encrypted messages end-to-end (with screenshots), plus how to run two instances locally.

  • Doc-test tutorials — executable walkthroughs that CI runs and publishes as an HTML report under https://logos-co.github.io/logos-chat-ui/: The Logos Chat UI (connect + share your address) and Run the automated message-exchange test (the real two-party round-trip, captured).

    Enabling the report links is a one-time repo setup: Settings -> Pages -> "Deploy from a branch", branch gh-pages / (root) (the CI publish-report job creates the gh-pages branch on its first run).

Module Structure

logos-chat-ui/
├── flake.nix                  # mkLogosQmlModule
├── metadata.json              # Module config (ui_qml, interface: universal)
├── CMakeLists.txt             # logos_module() macro
└── src/
    ├── ChatBackend.rep        # QtRO interface (ChatStatus enum, props, slots, signals)
    ├── ChatBackend.h/cpp      # Backend: chat lifecycle, conversations, messages
    ├── ConversationListModel.h/cpp  # QAbstractListModel for conversations
    ├── MessageListModel.h/cpp       # QAbstractListModel for messages
    └── qml/
        ├── ChatView.qml       # Top-level composition (thin)
        └── ChatUi/            # Pure-QML component module, built on Logos.Theme
            ├── ChatStore.qml          # Sole reader of the injected logos context
            ├── ConversationsPane.qml  # Conversation list (left)
            ├── MessageThreadPane.qml  # Message thread + composer (center)
            ├── MembersPane.qml        # Group roster + add-member (right)
            ├── ...                    # dialogs, delegates, leaf components
            └── qmldir

The plugin entry point and QtRO replica/source glue are generated by mkLogosQmlModule from metadata.json#codegen (rep / backend_class / backend_header); the repo carries the backend, the two models, and the QML view module.

Key Components

File Role
ChatBackend.rep Defines the C++/QML boundary — ChatStatus enum, state props, lifecycle slots, signals
ChatBackend Derives ChatBackendSimpleSource + LogosUiPluginContext; initialises the module and subscribes to chat_module events in onContextReady(); drives the two models
ConversationListModel Roles: conversationId, displayName, lastActivity, unreadCount
MessageListModel Roles: sender, content, timestamp, isMe

Requirements

Tip

When using Nix, all requirements are acquired automatically.

Dependencies

Dependency Purpose
Qt6 Core, RemoteObjects, Declarative UI framework + IPC
logos-module-builder Build system (mkLogosQmlModule)
logos-chat-module Chat backend module
logos-delivery-module Transport (Waku) — runtime dependency, pinned at v0.1.3
Repository Role
logos-chat-module Chat backend — this UI's required dependency
logos-delivery-module Transport (Waku) — runtime dependency, pinned at v0.1.3
libchat Chat engine embedded by chat_module (E2EE, sessions)
logos-module-builder Module build system
logos-liblogos Logos Core platform
S
Description
UI App for Logos ChatSDK POC
Readme
16 MiB
Languages
QML 56.7%
C++ 25.5%
JavaScript 9.5%
Shell 5.6%
CMake 1.3%
Other 1.4%