docs: add crate-level documentation and public API doc comments

- Expand crate-level docs for quicprochat-rpc (architecture, wire format,
  module map) and quicprochat-sdk (connection lifecycle, event subscription,
  module descriptions).
- Add /// doc comments to all undocumented pub fn/struct/enum items in
  server domain services (keys, channels, devices, users, account, p2p,
  blobs) and domain types.
- Fix rustdoc broken intra-doc links in plugin-api (HookResult,
  qpc_plugin_init), federation/mod.rs (Store), and client main.rs
  (unescaped brackets).
This commit is contained in:
2026-03-09 20:46:54 +01:00
parent 416618f4cf
commit c256c38ffb
13 changed files with 171 additions and 13 deletions

View File

@@ -505,7 +505,7 @@ enum Command {
password: Option<String>,
},
/// Interactive 1:1 chat: type to send, incoming messages printed as [peer] <msg>. Ctrl+D to exit.
/// Interactive 1:1 chat: type to send, incoming messages printed as \[peer\] msg. Ctrl+D to exit.
/// In a two-person group, peer is chosen automatically; use --peer-key only with 3+ members.
Chat {
#[arg(

View File

@@ -8,7 +8,7 @@
//! extern "C" int32_t qpc_plugin_init(HookVTable *vtable);
//! ```
//!
//! The server passes a zeroed [`HookVTable`] to `qpc_plugin_init`. The plugin
//! The server passes a zeroed [`HookVTable`] to the init function. The plugin
//! fills in whichever function pointers it cares about and returns `0` on
//! success or a negative error code on failure. Unused slots remain null and
//! the server treats them as no-ops.
@@ -27,9 +27,9 @@
//!
//! # Return values
//!
//! Hooks that can reject an operation return [`HookResult`]. The server maps
//! `HOOK_CONTINUE` to `HookAction::Continue` and any other value to
//! `HookAction::Reject` with the reason string from [`HookVTable::error_message`].
//! Hooks that can reject an operation return an `i32` result code. The server maps
//! [`HOOK_CONTINUE`] to allow and any other value to reject, reading the reason
//! string from [`HookVTable::error_message`].
#![no_std]
@@ -105,7 +105,7 @@ pub struct CFetchEvent {
// ── HookVTable ────────────────────────────────────────────────────────────────
/// C-ABI function-pointer table filled by [`qpc_plugin_init`].
/// C-ABI function-pointer table filled by the plugin's `qpc_plugin_init` export.
///
/// All fields default to null (no-op). The server only calls a slot when its
/// pointer is non-null. The `user_data` field is passed as the first argument

View File

@@ -1,9 +1,43 @@
//! QUIC RPC framework for quicprochat v2.
//!
//! Wire format per QUIC stream:
//! - Request: `[method_id: u16][request_id: u32][payload_len: u32][protobuf bytes]`
//! - Response: `[status: u8][request_id: u32][payload_len: u32][protobuf bytes]`
//! - Push: `[event_type: u16][payload_len: u32][protobuf bytes]` (uni-stream)
//! This crate implements a lightweight, binary-framed RPC protocol over QUIC.
//! Each RPC call opens a new QUIC bi-directional stream, sends a request frame,
//! and reads a response frame. Server-initiated push events use uni-streams.
//!
//! # Wire format
//!
//! All integers are big-endian.
//!
//! **Request** (client → server, bi-stream):
//! ```text
//! [method_id: u16][request_id: u32][payload_len: u32][protobuf bytes]
//! ```
//!
//! **Response** (server → client, same bi-stream):
//! ```text
//! [status: u8][request_id: u32][payload_len: u32][protobuf bytes]
//! ```
//!
//! **Push** (server → client, uni-stream):
//! ```text
//! [event_type: u16][payload_len: u32][protobuf bytes]
//! ```
//!
//! # Architecture
//!
//! - [`framing`] — frame types ([`framing::RequestFrame`], [`framing::ResponseFrame`],
//! [`framing::PushFrame`]) and encode/decode logic.
//! - [`method`] — [`method::MethodRegistry`] maps `u16` method IDs to async handler
//! functions with optional per-method timeouts.
//! - [`server`] — [`server::RpcServer`] binds a QUIC endpoint, performs an auth
//! handshake on each connection, then dispatches RPC streams to registered handlers.
//! - [`client`] — [`client::RpcClient`] connects to the server with TLS, supports
//! auto-reconnect with exponential backoff, and multiplexes concurrent RPCs.
//! - [`push`] — [`push::PushBroker`] manages per-identity push connections and
//! fan-out to channel members.
//! - [`middleware`] — session validation, rate limiting, and structured audit logging.
//! - [`auth_handshake`] — initial session-token exchange on the first bi-stream.
//! - [`error`] — [`error::RpcError`] and [`error::RpcStatus`] types.
pub mod auth_handshake;
pub mod framing;

View File

@@ -1,7 +1,44 @@
//! Client SDK for quicprochat v2.
//!
//! Provides `QpqClient` — a single entry point for connecting, authenticating,
//! sending/receiving messages, and subscribing to real-time events.
//! Provides [`QpqClient`](client::QpqClient) — a single entry point for
//! connecting to a quicprochat server, authenticating via OPAQUE, managing
//! conversations, and subscribing to real-time events.
//!
//! # Connection lifecycle
//!
//! 1. Create a [`QpqClient`](client::QpqClient) with a [`ClientConfig`](config::ClientConfig).
//! 2. Call [`connect()`](client::QpqClient::connect) to establish a QUIC connection
//! and open the local SQLCipher conversation store.
//! 3. [`register()`](client::QpqClient::register) or
//! [`login()`](client::QpqClient::login) via OPAQUE to authenticate.
//! 4. Optionally call [`start_heartbeat()`](client::QpqClient::start_heartbeat)
//! to enable proactive dead-connection detection.
//! 5. Use messaging, group, key, and device APIs through the client.
//! 6. Call [`disconnect()`](client::QpqClient::disconnect) to shut down gracefully.
//!
//! # Event subscription
//!
//! Call [`subscribe()`](client::QpqClient::subscribe) to get a
//! `tokio::sync::broadcast::Receiver<ClientEvent>` that emits connection state
//! changes, authentication events, and incoming message notifications.
//!
//! # Modules
//!
//! - [`client`] — [`QpqClient`](client::QpqClient) and connection management.
//! - [`auth`] — OPAQUE registration and login flows.
//! - [`config`] — [`ClientConfig`](config::ClientConfig) with server address, TLS, DB path.
//! - [`conversation`] — [`ConversationStore`](conversation::ConversationStore) for local
//! message persistence and user blocking.
//! - [`events`] — [`ClientEvent`](events::ClientEvent) enum for the event bus.
//! - [`groups`] — group membership management.
//! - [`keys`] — MLS KeyPackage and hybrid (PQ) key upload/fetch.
//! - [`messaging`] — send and receive encrypted messages.
//! - [`devices`] — multi-device registration, listing, and revocation.
//! - [`recovery`] — account recovery bundle upload/fetch.
//! - [`outbox`] — offline message queue with retry.
//! - [`state`] — client state persistence (identity key, session token).
//! - [`transcript`] — conversation transcript export.
//! - [`users`] — username/identity resolution.
pub mod auth;
pub mod client;

View File

@@ -15,6 +15,7 @@ pub struct AccountService {
}
impl AccountService {
/// Delete an account and append a KT tombstone entry for auditability.
pub fn delete_account(&self, caller_identity_key: &[u8]) -> Result<(), DomainError> {
self.store.delete_account(caller_identity_key)?;

View File

@@ -31,6 +31,9 @@ impl BlobService {
self.data_dir.join("blobs")
}
/// Upload a blob chunk. Chunks are written at the given offset. When the
/// final chunk completes the upload, the SHA-256 hash is verified and the
/// blob is finalized. Already-complete blobs return immediately.
pub fn upload_blob(
&self,
req: UploadBlobReq,
@@ -136,6 +139,7 @@ impl BlobService {
})
}
/// Download a chunk of a stored blob. Reads at most 256 KB per call.
pub fn download_blob(
&self,
req: DownloadBlobReq,

View File

@@ -12,6 +12,10 @@ pub struct ChannelService {
}
impl ChannelService {
/// Create or look up a 1:1 DM channel between caller and peer.
///
/// The channel ID is deterministic and symmetric — `create(A, B)` returns
/// the same ID as `create(B, A)`. Returns `was_new = true` on first creation.
pub fn create_channel(
&self,
req: CreateChannelReq,

View File

@@ -14,6 +14,7 @@ pub struct DeviceService {
}
impl DeviceService {
/// Register a new device for the caller's identity. Enforces a per-identity device limit.
pub fn register_device(
&self,
req: RegisterDeviceReq,
@@ -37,6 +38,7 @@ impl DeviceService {
Ok(RegisterDeviceResp { success })
}
/// List all registered devices for the caller's identity.
pub fn list_devices(
&self,
caller_identity_key: &[u8],
@@ -53,6 +55,7 @@ impl DeviceService {
Ok(ListDevicesResp { devices })
}
/// Revoke (remove) a registered device. Returns an error if the device is not found.
pub fn revoke_device(
&self,
req: RevokeDeviceReq,

View File

@@ -16,6 +16,7 @@ pub struct KeyService {
}
impl KeyService {
/// Upload an MLS KeyPackage for the given identity. Returns a SHA-256 fingerprint.
pub fn upload_key_package(
&self,
req: UploadKeyPackageReq,
@@ -40,6 +41,7 @@ impl KeyService {
Ok(UploadKeyPackageResp { fingerprint })
}
/// Fetch the stored MLS KeyPackage for an identity. Returns empty if none exists.
pub fn fetch_key_package(
&self,
req: FetchKeyPackageReq,
@@ -53,6 +55,7 @@ impl KeyService {
})
}
/// Upload a hybrid (ML-KEM-768) public key for post-quantum key exchange.
pub fn upload_hybrid_key(
&self,
req: UploadHybridKeyReq,
@@ -70,6 +73,7 @@ impl KeyService {
Ok(())
}
/// Fetch the hybrid public key for a single identity. Returns empty if none exists.
pub fn fetch_hybrid_key(
&self,
req: FetchHybridKeyReq,
@@ -82,6 +86,7 @@ impl KeyService {
Ok(FetchHybridKeyResp { hybrid_public_key })
}
/// Batch-fetch hybrid public keys for multiple identities. Missing keys return empty.
pub fn fetch_hybrid_keys(
&self,
req: FetchHybridKeysReq,

View File

@@ -12,6 +12,7 @@ pub struct P2pService {
}
impl P2pService {
/// Publish a P2P endpoint (iroh node address) for the caller's identity.
pub fn publish_endpoint(
&self,
req: PublishEndpointReq,
@@ -26,6 +27,7 @@ impl P2pService {
Ok(())
}
/// Resolve a P2P endpoint for an identity key. Returns empty if none published.
pub fn resolve_endpoint(
&self,
req: ResolveEndpointReq,
@@ -42,6 +44,7 @@ impl P2pService {
Ok(ResolveEndpointResp { node_addr })
}
/// Return a health-check response (always "ok").
pub fn health() -> HealthResp {
HealthResp {
status: "ok".into(),

View File

@@ -74,6 +74,7 @@ pub struct RegisterStartReq {
pub request_bytes: Vec<u8>,
}
/// OPAQUE registration start response.
pub struct RegisterStartResp {
pub response_bytes: Vec<u8>,
}
@@ -85,6 +86,7 @@ pub struct RegisterFinishReq {
pub identity_key: Vec<u8>,
}
/// OPAQUE registration finish response.
pub struct RegisterFinishResp {
pub success: bool,
}
@@ -95,6 +97,7 @@ pub struct LoginStartReq {
pub request_bytes: Vec<u8>,
}
/// OPAQUE login start response.
pub struct LoginStartResp {
pub response_bytes: Vec<u8>,
}
@@ -106,6 +109,7 @@ pub struct LoginFinishReq {
pub identity_key: Vec<u8>,
}
/// OPAQUE login finish response containing the session token.
pub struct LoginFinishResp {
pub session_token: Vec<u8>,
}
@@ -119,6 +123,7 @@ pub struct Envelope {
pub data: Vec<u8>,
}
/// Request to enqueue a message for delivery to a recipient.
pub struct EnqueueReq {
pub recipient_key: Vec<u8>,
pub payload: Vec<u8>,
@@ -126,37 +131,44 @@ pub struct EnqueueReq {
pub ttl_secs: u32,
}
/// Response from enqueue with the assigned sequence number.
pub struct EnqueueResp {
pub seq: u64,
pub delivery_proof: Vec<u8>,
}
/// Request to fetch (and drain) queued messages.
pub struct FetchReq {
pub recipient_key: Vec<u8>,
pub channel_id: Vec<u8>,
pub limit: u32,
}
/// Response containing fetched message envelopes.
pub struct FetchResp {
pub payloads: Vec<Envelope>,
}
/// Request to peek at queued messages without draining.
pub struct PeekReq {
pub recipient_key: Vec<u8>,
pub channel_id: Vec<u8>,
pub limit: u32,
}
/// Response containing peeked message envelopes.
pub struct PeekResp {
pub payloads: Vec<Envelope>,
}
/// Request to acknowledge messages up to a sequence number.
pub struct AckReq {
pub recipient_key: Vec<u8>,
pub channel_id: Vec<u8>,
pub seq_up_to: u64,
}
/// Request to enqueue a message to multiple recipients at once.
pub struct BatchEnqueueReq {
pub recipient_keys: Vec<Vec<u8>>,
pub payload: Vec<u8>,
@@ -164,83 +176,100 @@ pub struct BatchEnqueueReq {
pub ttl_secs: u32,
}
/// Response from batch enqueue with one sequence number per recipient.
pub struct BatchEnqueueResp {
pub seqs: Vec<u64>,
}
// ── Keys ─────────────────────────────────────────────────────────────────────
/// Request to upload an MLS KeyPackage.
pub struct UploadKeyPackageReq {
pub identity_key: Vec<u8>,
pub package: Vec<u8>,
}
/// Response containing the SHA-256 fingerprint of the uploaded KeyPackage.
#[derive(Debug)]
pub struct UploadKeyPackageResp {
pub fingerprint: Vec<u8>,
}
/// Request to fetch a stored MLS KeyPackage.
pub struct FetchKeyPackageReq {
pub identity_key: Vec<u8>,
}
/// Response containing the MLS KeyPackage (empty if not found).
pub struct FetchKeyPackageResp {
pub package: Vec<u8>,
}
/// Request to upload a hybrid (ML-KEM-768) public key.
pub struct UploadHybridKeyReq {
pub identity_key: Vec<u8>,
pub hybrid_public_key: Vec<u8>,
}
/// Request to fetch a single hybrid public key.
pub struct FetchHybridKeyReq {
pub identity_key: Vec<u8>,
}
/// Response containing a hybrid public key (empty if not found).
pub struct FetchHybridKeyResp {
pub hybrid_public_key: Vec<u8>,
}
/// Request to batch-fetch hybrid public keys for multiple identities.
pub struct FetchHybridKeysReq {
pub identity_keys: Vec<Vec<u8>>,
}
/// Response containing hybrid keys in the same order as the request.
pub struct FetchHybridKeysResp {
pub keys: Vec<Vec<u8>>,
}
// ── Key Transparency / Revocation ────────────────────────────────────
/// Request to revoke an identity key in the Key Transparency log.
pub struct RevokeKeyReq {
pub identity_key: Vec<u8>,
pub reason: String,
}
/// Response from key revocation with the leaf index in the KT log.
pub struct RevokeKeyResp {
pub success: bool,
pub leaf_index: u64,
}
/// Request to check if an identity key has been revoked.
pub struct CheckRevocationReq {
pub identity_key: Vec<u8>,
}
/// Response with revocation status, reason, and timestamp.
pub struct CheckRevocationResp {
pub revoked: bool,
pub reason: String,
pub timestamp_ms: u64,
}
/// Request for a range of Key Transparency audit log entries.
pub struct AuditKeyTransparencyReq {
pub start: u64,
pub end: u64,
}
/// A single entry in the KT audit log.
pub struct AuditLogEntry {
pub index: u64,
pub leaf_hash: Vec<u8>,
}
/// Response containing KT audit log entries, tree size, and root hash.
pub struct AuditKeyTransparencyResp {
pub entries: Vec<AuditLogEntry>,
pub tree_size: u64,
@@ -249,10 +278,12 @@ pub struct AuditKeyTransparencyResp {
// ── Channel ──────────────────────────────────────────────────────────────────
/// Request to create a 1:1 DM channel with a peer.
pub struct CreateChannelReq {
pub peer_key: Vec<u8>,
}
/// Response with the channel ID and whether it was newly created.
#[derive(Debug)]
pub struct CreateChannelResp {
pub channel_id: Vec<u8>,
@@ -261,25 +292,30 @@ pub struct CreateChannelResp {
// ── User ─────────────────────────────────────────────────────────────────────
/// Request to resolve a username to an identity key.
pub struct ResolveUserReq {
pub username: String,
}
/// Response with the identity key and KT inclusion proof.
pub struct ResolveUserResp {
pub identity_key: Vec<u8>,
pub inclusion_proof: Vec<u8>,
}
/// Request to reverse-resolve an identity key to a username.
pub struct ResolveIdentityReq {
pub identity_key: Vec<u8>,
}
/// Response with the resolved username (empty if unknown).
pub struct ResolveIdentityResp {
pub username: String,
}
// ── Blob ─────────────────────────────────────────────────────────────────────
/// Request to upload a blob chunk. The blob is identified by its SHA-256 hash.
pub struct UploadBlobReq {
pub blob_hash: Vec<u8>,
pub chunk: Vec<u8>,
@@ -288,16 +324,19 @@ pub struct UploadBlobReq {
pub mime_type: String,
}
/// Response from blob upload containing the blob ID (same as hash).
pub struct UploadBlobResp {
pub blob_id: Vec<u8>,
}
/// Request to download a chunk of a stored blob.
pub struct DownloadBlobReq {
pub blob_id: Vec<u8>,
pub offset: u64,
pub length: u32,
}
/// Response containing the requested blob chunk and metadata.
pub struct DownloadBlobResp {
pub chunk: Vec<u8>,
pub total_size: u64,
@@ -306,35 +345,42 @@ pub struct DownloadBlobResp {
// ── Device ───────────────────────────────────────────────────────────────────
/// Request to register a new device for the caller's identity.
pub struct RegisterDeviceReq {
pub device_id: Vec<u8>,
pub device_name: String,
}
/// Response from device registration.
pub struct RegisterDeviceResp {
pub success: bool,
}
/// Metadata about a registered device.
pub struct DeviceInfo {
pub device_id: Vec<u8>,
pub device_name: String,
pub registered_at: u64,
}
/// Response listing all registered devices.
pub struct ListDevicesResp {
pub devices: Vec<DeviceInfo>,
}
/// Request to revoke (remove) a registered device.
pub struct RevokeDeviceReq {
pub device_id: Vec<u8>,
}
/// Response from device revocation.
pub struct RevokeDeviceResp {
pub success: bool,
}
// ── Group metadata ───────────────────────────────────────────────────
/// Server-side group metadata.
pub struct GroupMetadata {
pub group_id: Vec<u8>,
pub name: String,
@@ -344,6 +390,7 @@ pub struct GroupMetadata {
pub created_at: u64,
}
/// Request to update group metadata (name, description, avatar).
pub struct UpdateGroupMetadataReq {
pub group_id: Vec<u8>,
pub name: String,
@@ -351,55 +398,66 @@ pub struct UpdateGroupMetadataReq {
pub avatar_hash: Vec<u8>,
}
/// Request to list members of a group.
pub struct ListGroupMembersReq {
pub group_id: Vec<u8>,
}
/// A group member with resolved username.
pub struct GroupMemberInfo {
pub identity_key: Vec<u8>,
pub username: String,
pub joined_at: u64,
}
/// Response listing group members.
pub struct ListGroupMembersResp {
pub members: Vec<GroupMemberInfo>,
}
// ── Moderation ───────────────────────────────────────────────────────────────
/// Request to submit an encrypted abuse report.
pub struct ReportMessageReq {
pub encrypted_report: Vec<u8>,
pub conversation_id: Vec<u8>,
pub reporter_identity: Vec<u8>,
}
/// Response from report submission.
pub struct ReportMessageResp {
pub accepted: bool,
}
/// Request to ban a user. `duration_secs = 0` means permanent.
pub struct BanUserReq {
pub identity_key: Vec<u8>,
pub reason: String,
pub duration_secs: u64,
}
/// Response from ban operation.
pub struct BanUserResp {
pub success: bool,
}
/// Request to unban a user.
pub struct UnbanUserReq {
pub identity_key: Vec<u8>,
}
/// Response from unban operation.
pub struct UnbanUserResp {
pub success: bool,
}
/// Request to list abuse reports with pagination.
pub struct ListReportsReq {
pub limit: u32,
pub offset: u32,
}
/// A stored abuse report entry.
pub struct ReportEntry {
pub id: u64,
pub encrypted_report: Vec<u8>,
@@ -408,10 +466,12 @@ pub struct ReportEntry {
pub timestamp: u64,
}
/// Response containing paginated abuse reports.
pub struct ListReportsResp {
pub reports: Vec<ReportEntry>,
}
/// A banned user entry with expiration info.
pub struct BannedUserEntry {
pub identity_key: Vec<u8>,
pub reason: String,
@@ -419,25 +479,30 @@ pub struct BannedUserEntry {
pub expires_at: u64,
}
/// Response listing all currently banned users.
pub struct ListBannedResp {
pub users: Vec<BannedUserEntry>,
}
// ── P2P ──────────────────────────────────────────────────────────────────────
/// Request to publish a P2P endpoint (iroh node address).
pub struct PublishEndpointReq {
pub identity_key: Vec<u8>,
pub node_addr: Vec<u8>,
}
/// Request to resolve a P2P endpoint for an identity.
pub struct ResolveEndpointReq {
pub identity_key: Vec<u8>,
}
/// Response with the resolved P2P node address (empty if not published).
pub struct ResolveEndpointResp {
pub node_addr: Vec<u8>,
}
/// Health-check response.
pub struct HealthResp {
pub status: String,
}

View File

@@ -16,6 +16,7 @@ pub struct UserService {
}
impl UserService {
/// Resolve a username to its identity key, with a KT inclusion proof if available.
pub fn resolve_user(&self, req: ResolveUserReq) -> Result<ResolveUserResp, DomainError> {
if req.username.is_empty() {
return Err(DomainError::EmptyUsername);
@@ -46,6 +47,7 @@ impl UserService {
})
}
/// Reverse-resolve an identity key to its username.
pub fn resolve_identity(
&self,
req: ResolveIdentityReq,

View File

@@ -3,7 +3,7 @@
//! When federation is enabled, the server binds a second QUIC endpoint on a
//! dedicated port (default 7001) that only accepts connections from known peers
//! authenticated via mTLS. Inbound requests are handled by [`service::FederationServiceImpl`],
//! which delegates to the local [`Store`]. Outbound relay uses [`client::FederationClient`].
//! which delegates to the local store. Outbound relay uses [`client::FederationClient`].
pub mod address;
pub mod client;