mirror of
https://github.com/logos-messaging/libchat.git
synced 2026-05-12 04:59:27 +00:00
chore(chat-cli): switch transport at runtime via --transport flag
Both file and logos-delivery transports are now compiled into a single binary and selected at runtime (default: logos-delivery), replacing the env-var-driven build-time cfg.
This commit is contained in:
parent
eaeffcd21f
commit
9e5f5573cb
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@ -16,8 +16,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup update stable && rustup default stable
|
||||
- run: cargo build --verbose
|
||||
- run: cargo test --verbose
|
||||
# chat-cli's build.rs unconditionally links liblogosdelivery and requires
|
||||
# LOGOS_DELIVERY_LIB_DIR. The smoketest job builds and exercises it under
|
||||
# Nix; here we keep the toolchain-only job fast by skipping it.
|
||||
- run: cargo build --verbose --workspace --exclude chat-cli
|
||||
- run: cargo test --verbose --workspace --exclude chat-cli
|
||||
|
||||
clippy:
|
||||
name: Clippy
|
||||
@ -26,7 +29,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- run: rustup update stable && rustup default stable
|
||||
- run: rustup component add clippy
|
||||
- run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
- run: cargo clippy --all-targets --all-features --workspace --exclude chat-cli -- -D warnings
|
||||
|
||||
fmt:
|
||||
name: Format
|
||||
|
||||
@ -4,16 +4,17 @@ Supporting library for Logos-chat
|
||||
## Example app
|
||||
|
||||
[`bin/chat-cli`](bin/chat-cli/) is an end-to-end encrypted CLI chat app
|
||||
built on this library. It uses [logos-delivery](https://github.com/logos-messaging/logos-delivery)
|
||||
built on this library. By default it uses [logos-delivery](https://github.com/logos-messaging/logos-delivery)
|
||||
(Waku-based) as the transport so two users anywhere in the world can chat by
|
||||
sharing an intro bundle.
|
||||
sharing an intro bundle. A local file transport is also bundled in; pick at
|
||||
runtime with `--transport <logos-delivery|file>`.
|
||||
|
||||
```sh
|
||||
# Build logos-delivery with Nix
|
||||
nix build .#logos-delivery
|
||||
# Build chat-cli with Cargo
|
||||
LOGOS_DELIVERY_LIB_DIR=./result/lib cargo build --release -p chat-cli
|
||||
# Run binary
|
||||
# Run binary (defaults to --transport logos-delivery)
|
||||
./target/release/chat-cli --name alice
|
||||
```
|
||||
|
||||
|
||||
@ -21,6 +21,3 @@ base64 = "0.22"
|
||||
thiserror = "2"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(logos_delivery)'] }
|
||||
|
||||
@ -4,8 +4,6 @@ A terminal chat application built on top of libchat. End-to-end encrypted messag
|
||||
|
||||
## Building
|
||||
|
||||
### With logos-delivery transport (recommended)
|
||||
|
||||
[logos-delivery](https://github.com/logos-messaging/logos-delivery) is exposed as a Nix package.
|
||||
Build it once, then point `LOGOS_DELIVERY_LIB_DIR` at the result:
|
||||
|
||||
@ -16,31 +14,35 @@ LOGOS_DELIVERY_LIB_DIR=./result/lib cargo build --release -p chat-cli
|
||||
|
||||
The binary lands at `target/release/chat-cli`.
|
||||
|
||||
### File transport only (no Nix required)
|
||||
|
||||
```bash
|
||||
cargo build --release -p chat-cli
|
||||
```
|
||||
|
||||
## Transports
|
||||
|
||||
| Transport | Description |
|
||||
|-----------|-------------|
|
||||
| File (default) | Shared directory; no network needed — great for local testing |
|
||||
| logos-delivery | Embedded Waku node on the logos.dev network |
|
||||
Both transports are compiled into the binary and selected at runtime via `--transport`:
|
||||
|
||||
The transport is selected automatically at compile time: if `LOGOS_DELIVERY_LIB_DIR` is set when building, logos-delivery is used; otherwise the file transport is used.
|
||||
| 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 (file transport)
|
||||
## Quick start
|
||||
|
||||
Run two instances in separate terminals, pointing at the same data directory:
|
||||
Run two instances in separate terminals:
|
||||
|
||||
```bash
|
||||
# Terminal 1
|
||||
cargo run -p chat-cli -- --name alice
|
||||
cargo run -p chat-cli -- --name alice --port 60001
|
||||
|
||||
# Terminal 2
|
||||
cargo run -p chat-cli -- --name bob
|
||||
cargo run -p chat-cli -- --name bob --port 60002
|
||||
```
|
||||
|
||||
For local-only testing without any network dependency, use the file transport:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
@ -49,20 +51,14 @@ cargo run -p chat-cli -- --name bob
|
||||
2. In Bob's terminal, type `/connect <paste bundle here>`.
|
||||
3. Bob's "Hello!" message appears in Alice's terminal. Both can now chat.
|
||||
|
||||
## logos-delivery transport
|
||||
|
||||
After building with `LOGOS_DELIVERY_LIB_DIR` set, run:
|
||||
|
||||
```bash
|
||||
./target/release/chat-cli --name alice
|
||||
```
|
||||
|
||||
Optional flags:
|
||||
## Options
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--db <path>` | *(ephemeral)* | SQLite file for persistent identity across restarts |
|
||||
| `--preset <name>` | `logos.dev` | Network preset (`logos.dev` or `twn`) |
|
||||
| `--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 |
|
||||
| `--log-file <path>` | *(stderr, off)* | Write logs to a file instead of stderr |
|
||||
|
||||
@ -80,7 +76,7 @@ Optional flags:
|
||||
| `/clear` | Clear current chat's message history |
|
||||
| `/quit` · `Esc` · `Ctrl+C` | Exit |
|
||||
|
||||
## Storage (file transport)
|
||||
## Storage
|
||||
|
||||
All data lives under `tmp/chat-cli-data/` by default (override with `--data`):
|
||||
|
||||
@ -88,7 +84,7 @@ All data lives under `tmp/chat-cli-data/` by default (override with `--data`):
|
||||
|------|----------|
|
||||
| `<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 |
|
||||
| `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`.
|
||||
|
||||
@ -97,7 +93,7 @@ The SQLite database can be inspected with *DB Browser for SQLite*: password `cha
|
||||
```
|
||||
bin/chat-cli/
|
||||
├── src/
|
||||
│ ├── main.rs entry point, CLI arg parsing, transport selection
|
||||
│ ├── 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
|
||||
@ -105,5 +101,5 @@ bin/chat-cli/
|
||||
│ └── transport/
|
||||
│ ├── file.rs file-based transport
|
||||
│ └── logos_delivery.rs logos-delivery (Waku) transport + FFI
|
||||
└── build.rs links liblogosdelivery when LOGOS_DELIVERY_LIB_DIR is set
|
||||
└── build.rs links liblogosdelivery (LOGOS_DELIVERY_LIB_DIR required)
|
||||
```
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(logos_delivery)");
|
||||
println!("cargo:rerun-if-env-changed=LOGOS_DELIVERY_LIB_DIR");
|
||||
|
||||
let Ok(lib_dir) = std::env::var("LOGOS_DELIVERY_LIB_DIR") else {
|
||||
return;
|
||||
};
|
||||
let lib_dir = std::env::var("LOGOS_DELIVERY_LIB_DIR").expect(
|
||||
"LOGOS_DELIVERY_LIB_DIR must be set; build liblogosdelivery via \
|
||||
`nix build .#logos-delivery` and point this var at the result/lib directory",
|
||||
);
|
||||
|
||||
println!("cargo:rustc-cfg=logos_delivery");
|
||||
println!("cargo:rustc-link-search=native={lib_dir}");
|
||||
println!("cargo:rustc-link-lib=dylib=logosdelivery");
|
||||
|
||||
// Set rpath so the binary finds the shared library at runtime.
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
||||
match target_os.as_str() {
|
||||
"macos" => println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}"),
|
||||
"linux" => println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}"),
|
||||
"macos" | "linux" => println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}"),
|
||||
other => panic!("unsupported OS for logos-delivery transport: {other}"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,14 +3,22 @@ mod transport;
|
||||
mod ui;
|
||||
mod utils;
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
use clap::{Parser, ValueEnum};
|
||||
use client::DeliveryService;
|
||||
|
||||
use app::ChatApp;
|
||||
|
||||
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||
#[value(rename_all = "kebab-case")]
|
||||
enum TransportKind {
|
||||
File,
|
||||
LogosDelivery,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "chat-cli", about = "End-to-end encrypted terminal chat")]
|
||||
struct Cli {
|
||||
@ -18,17 +26,20 @@ struct Cli {
|
||||
#[arg(long, short)]
|
||||
name: String,
|
||||
|
||||
// ── File-transport options ────────────────────────────────────────────────
|
||||
/// Shared data directory for file transport (both peers must use the same path).
|
||||
/// Which delivery transport to use.
|
||||
#[arg(long, value_enum, default_value_t = TransportKind::LogosDelivery)]
|
||||
transport: TransportKind,
|
||||
|
||||
/// Data directory (used for UI state and the default SQLite path).
|
||||
#[arg(long, default_value = "tmp/chat-cli-data")]
|
||||
data: PathBuf,
|
||||
|
||||
// ── logos-delivery transport options ──────────────────────────────────────
|
||||
/// Persistent SQLite database for logos-delivery transport (omit for ephemeral identity).
|
||||
/// Override the SQLite database path (defaults to `<data>/<name>.db`).
|
||||
#[arg(long)]
|
||||
db: Option<PathBuf>,
|
||||
|
||||
/// logos-delivery network preset (`logos.dev` or `twn`).
|
||||
// ── logos-delivery transport options ──────────────────────────────────────
|
||||
/// logos-delivery network preset (e.g. `logos.dev`).
|
||||
#[arg(long, default_value = "logos.dev")]
|
||||
preset: String,
|
||||
|
||||
@ -48,30 +59,54 @@ struct Cli {
|
||||
fn main() -> Result<()> {
|
||||
let cli = Cli::parse();
|
||||
setup_logging(cli.log_file.as_deref())?;
|
||||
#[cfg(logos_delivery)]
|
||||
return run_logos_delivery(cli);
|
||||
#[cfg(not(logos_delivery))]
|
||||
run_file(cli)
|
||||
}
|
||||
|
||||
#[cfg(not(logos_delivery))]
|
||||
fn run_file(cli: Cli) -> Result<()> {
|
||||
use transport::file::FileTransport;
|
||||
|
||||
std::fs::create_dir_all(&cli.data).context("failed to create data directory")?;
|
||||
|
||||
println!("Starting chat as '{}'...", cli.name);
|
||||
println!("Data dir: {}", cli.data.display());
|
||||
match cli.transport {
|
||||
TransportKind::File => {
|
||||
let transport_dir = cli.data.join("transport");
|
||||
let (transport, inbound) = transport::file::FileTransport::new(&transport_dir)
|
||||
.context("failed to create file transport")?;
|
||||
run(transport, inbound, &cli)
|
||||
}
|
||||
TransportKind::LogosDelivery => {
|
||||
use transport::logos_delivery::{Config, Service};
|
||||
|
||||
let transport_dir = cli.data.join("transport");
|
||||
let (transport, inbound) =
|
||||
FileTransport::new(&transport_dir).context("failed to create file transport")?;
|
||||
eprintln!("Starting logos-delivery node (preset={})...", cli.preset);
|
||||
eprintln!("This may take a few seconds while connecting to the network.");
|
||||
|
||||
let cfg = Config {
|
||||
preset: cli.preset.clone(),
|
||||
tcp_port: cli.port,
|
||||
..Default::default()
|
||||
};
|
||||
let (transport, inbound) =
|
||||
Service::start(cfg).context("failed to start logos-delivery")?;
|
||||
|
||||
eprintln!("Node connected. Initializing chat client...");
|
||||
run(transport, inbound, &cli)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run<D: DeliveryService>(
|
||||
transport: D,
|
||||
inbound: mpsc::Receiver<Vec<u8>>,
|
||||
cli: &Cli,
|
||||
) -> Result<()> {
|
||||
let db_path = cli
|
||||
.db
|
||||
.clone()
|
||||
.unwrap_or_else(|| cli.data.join(format!("{}.db", cli.name)));
|
||||
let db_str = db_path
|
||||
.to_str()
|
||||
.context("db path contains non-UTF-8 characters")?
|
||||
.to_string();
|
||||
|
||||
let db_path = cli.data.join(format!("{}.db", cli.name));
|
||||
let client = client::ChatClient::open(
|
||||
cli.name.clone(),
|
||||
client::StorageConfig::Encrypted {
|
||||
path: db_path.to_string_lossy().to_string(),
|
||||
path: db_str,
|
||||
key: "chat-cli".to_string(),
|
||||
},
|
||||
transport,
|
||||
@ -91,71 +126,6 @@ fn run_file(cli: Cli) -> Result<()> {
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg_attr(not(logos_delivery), allow(dead_code, unused_variables))]
|
||||
fn run_logos_delivery(cli: Cli) -> Result<()> {
|
||||
#[cfg(logos_delivery)]
|
||||
{
|
||||
use transport::logos_delivery::{Config, Service};
|
||||
|
||||
eprintln!("Starting logos-delivery node (preset={})...", cli.preset);
|
||||
eprintln!("This may take a few seconds while connecting to the network.");
|
||||
|
||||
let logos_cfg = Config {
|
||||
preset: cli.preset.clone(),
|
||||
tcp_port: cli.port,
|
||||
..Default::default()
|
||||
};
|
||||
let (delivery, inbound) =
|
||||
Service::start(logos_cfg).context("failed to start logos-delivery")?;
|
||||
|
||||
eprintln!("Node connected. Initializing chat client...");
|
||||
|
||||
let data_dir = cli
|
||||
.db
|
||||
.as_ref()
|
||||
.and_then(|p| p.parent())
|
||||
.map(|p| p.to_path_buf())
|
||||
.unwrap_or_else(|| cli.data.clone());
|
||||
|
||||
let client = match cli.db {
|
||||
Some(ref path) => {
|
||||
let db_str = path
|
||||
.to_str()
|
||||
.context("db path contains non-UTF-8 characters")?
|
||||
.to_string();
|
||||
client::ChatClient::open(
|
||||
cli.name.clone(),
|
||||
client::StorageConfig::Encrypted {
|
||||
path: db_str,
|
||||
key: "chat-cli".to_string(),
|
||||
},
|
||||
delivery,
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("{e:?}"))
|
||||
.context("failed to open persistent client")?
|
||||
}
|
||||
None => client::ChatClient::new(cli.name.clone(), delivery),
|
||||
};
|
||||
|
||||
let mut app = ChatApp::new(client, inbound, &cli.name, &data_dir)?;
|
||||
|
||||
if cli.smoketest {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut terminal = ui::init().context("failed to initialize terminal")?;
|
||||
let result = run_app(&mut terminal, &mut app);
|
||||
ui::restore().context("failed to restore terminal")?;
|
||||
return result;
|
||||
}
|
||||
|
||||
#[cfg(not(logos_delivery))]
|
||||
anyhow::bail!(
|
||||
"logos-delivery transport is not available in this build.\n\
|
||||
Build with LOGOS_DELIVERY_LIB_DIR set to enable it."
|
||||
)
|
||||
}
|
||||
|
||||
fn run_app<D: DeliveryService>(terminal: &mut ui::Tui, app: &mut ChatApp<D>) -> Result<()> {
|
||||
loop {
|
||||
app.process_incoming()?;
|
||||
@ -167,7 +137,7 @@ fn run_app<D: DeliveryService>(terminal: &mut ui::Tui, app: &mut ChatApp<D>) ->
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_logging(log_file: Option<&std::path::Path>) -> Result<()> {
|
||||
fn setup_logging(log_file: Option<&Path>) -> Result<()> {
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("warn"));
|
||||
|
||||
@ -1,4 +1,2 @@
|
||||
#[cfg(not(logos_delivery))]
|
||||
pub mod file;
|
||||
#[cfg(logos_delivery)]
|
||||
pub mod logos_delivery;
|
||||
|
||||
@ -57,6 +57,7 @@
|
||||
nativeBuildInputs = [ pkgs.perl pkgs.pkg-config pkgs.cmake ];
|
||||
buildType = "release";
|
||||
doCheck = false;
|
||||
cargoBuildFlags = [ "--workspace" "--exclude" "chat-cli" ];
|
||||
|
||||
postBuild = ''
|
||||
cargo run --frozen --release --bin generate-headers --features headers -p client-ffi -- crates/client-ffi/client_ffi.h
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user