Files
quicproquo/crates/quicproquo-client/src/lib.rs
Chris Nennemann 612b06aa8e feat: Sprint 1 — production hardening, TLS lifecycle, CI coverage, lint cleanup
- Fix 3 client panics: replace .unwrap()/.expect() with proper error
  handling in rpc.rs (AUTH_CONTEXT lock), repl.rs (pending_member),
  and retry.rs (last_err)
- Add --danger-accept-invalid-certs flag with InsecureServerCertVerifier
  for development TLS bypass, plus mdBook TLS documentation
- Add CI coverage job (cargo-tarpaulin) and Docker build validation
  to GitHub Actions workflow, plus README CI badge
- Add [workspace.lints] config, fix 46 clippy warnings across 8 crates,
  zero warnings on all buildable crates
- Update Dockerfile for all 11 workspace members
2026-03-03 23:19:11 +01:00

82 lines
3.0 KiB
Rust

//! quicproquo CLI client library.
//!
//! # KeyPackage expiry and refresh
//!
//! KeyPackages are single-use (consumed when someone fetches them for an invite) and the server
//! may enforce a TTL (e.g. 24 hours). To stay invitable, run `qpq refresh-keypackage`
//! periodically (e.g. before the server TTL) or after your KeyPackage was consumed:
//!
//! ```bash
//! qpq refresh-keypackage --state qpq-state.bin --server 127.0.0.1:7000
//! ```
//!
//! Use the same `--access-token` (or `QPQ_ACCESS_TOKEN`) as for other authenticated
//! commands. See the [running-the-client](https://docs.quicproquo.dev/getting-started/running-the-client)
//! docs for details.
use std::sync::RwLock;
use std::sync::atomic::{AtomicBool, Ordering};
pub mod client;
pub use client::commands::{
cmd_chat, cmd_check_key, cmd_create_group, cmd_demo_group, cmd_export, cmd_export_verify,
cmd_fetch_key, cmd_health, cmd_health_json, cmd_invite, cmd_join, cmd_login, cmd_ping,
cmd_recv, cmd_register, cmd_register_state, cmd_refresh_keypackage, cmd_register_user,
cmd_send, cmd_whoami, opaque_login, receive_pending_plaintexts, whoami_json,
};
pub use client::repl::run_repl;
pub use client::rpc::{connect_node, connect_node_opt, create_channel, enqueue, fetch_wait, resolve_user};
// Global auth context — RwLock so the REPL can set it after OPAQUE login.
pub(crate) static AUTH_CONTEXT: RwLock<Option<ClientAuth>> = RwLock::new(None);
/// When `true`, [`connect_node`] skips TLS certificate verification.
/// Set via [`set_insecure_skip_verify`]; read by the RPC layer.
pub(crate) static INSECURE_SKIP_VERIFY: AtomicBool = AtomicBool::new(false);
/// Enable or disable insecure (no-verify) TLS mode globally.
///
/// **Development only.** When enabled, all outgoing connections skip certificate
/// verification, making them vulnerable to MITM attacks.
pub fn set_insecure_skip_verify(enabled: bool) {
INSECURE_SKIP_VERIFY.store(enabled, Ordering::Relaxed);
}
#[derive(Clone, Debug)]
pub struct ClientAuth {
pub(crate) version: u16,
pub(crate) access_token: Vec<u8>,
pub(crate) device_id: Vec<u8>,
}
impl ClientAuth {
/// Build a client auth context from optional token and device id.
pub fn from_parts(access_token: String, device_id: Option<String>) -> Self {
let token = access_token.into_bytes();
let device = device_id.unwrap_or_default().into_bytes();
Self {
version: 1,
access_token: token,
device_id: device,
}
}
/// Build from raw token bytes (e.g. a 32-byte OPAQUE session token).
pub fn from_raw(raw_token: Vec<u8>, device_id: Option<String>) -> Self {
let device = device_id.unwrap_or_default().into_bytes();
Self {
version: 1,
access_token: raw_token,
device_id: device,
}
}
}
/// Set (or replace) the global auth context.
pub fn init_auth(ctx: ClientAuth) {
let mut guard = AUTH_CONTEXT.write().expect("AUTH_CONTEXT poisoned");
*guard = Some(ctx);
}