chore: rename quicproquo → quicprochat in docs, Docker, CI, and packaging

Rename all project references from quicproquo/qpq to quicprochat/qpc
across documentation, Docker configuration, CI workflows, packaging
scripts, operational configs, and build tooling.

- Docker: crate paths, binary names, user/group, data dirs, env vars
- CI: workflow crate references, binary names, artifact names
- Docs: all markdown files under docs/, SDK READMEs, book.toml
- Packaging: OpenWrt Makefile, init script, UCI config (file renames)
- Scripts: justfile, dev-shell, screenshot, cross-compile, ai_team
- Operations: Prometheus config, alert rules, Grafana dashboard
- Config: .env.example (QPQ_* → QPC_*), CODEOWNERS paths
- Top-level: README, CONTRIBUTING, ROADMAP, CLAUDE.md
This commit is contained in:
2026-03-07 18:46:43 +01:00
parent a710037dde
commit 2e081ead8e
179 changed files with 1645 additions and 1645 deletions

View File

@@ -0,0 +1,155 @@
import Foundation
import CQuicProChat
/// High-level quicprochat client for iOS/macOS.
///
/// Wraps ``libquicprochat_ffi`` to provide a Swift-native API for
/// connecting, authenticating, and messaging.
///
/// ```swift
/// let client = try QpqClient(
/// server: "127.0.0.1:5001",
/// caCertPath: "/path/to/ca.pem"
/// )
/// try client.login(username: "alice", password: "secret")
/// try client.send(to: "bob", message: "hello".data(using: .utf8)!)
/// let messages = try client.receive(timeoutMs: 5000)
/// client.disconnect()
/// ```
public final class QpqClient: @unchecked Sendable {
private var handle: OpaquePointer?
private let lock = NSLock()
/// Connect to a quicprochat server.
///
/// - Parameters:
/// - server: Server address as ``host:port``.
/// - caCertPath: Path to the PEM-encoded CA certificate.
/// - serverName: TLS SNI server name (defaults to host from *server*).
/// - Throws: ``QpqError/connectionFailed(_:)`` if the connection fails.
public init(server: String, caCertPath: String, serverName: String? = nil) throws {
let sn = serverName ?? server.components(separatedBy: ":").first ?? server
let h = qpq_connect(server, caCertPath, sn)
guard let h else {
throw QpqError.connectionFailed("qpq_connect returned NULL for \(server)")
}
self.handle = h
}
deinit {
disconnect()
}
// MARK: - Authentication
/// Authenticate with the server using OPAQUE (username + password).
///
/// - Throws: ``QpqError/authFailed(_:)`` on bad credentials.
public func login(username: String, password: String) throws {
try withHandle { h in
let code = qpq_login(h, username, password)
try checkStatus(code, handle: h)
}
}
// MARK: - Messaging
/// Send a message to a recipient by username.
///
/// The message is encrypted via MLS before delivery.
///
/// - Parameters:
/// - recipient: Recipient username.
/// - message: Message payload (arbitrary bytes).
public func send(to recipient: String, message: Data) throws {
try withHandle { h in
let code = message.withUnsafeBytes { buffer -> Int32 in
guard let ptr = buffer.baseAddress?.assumingMemoryBound(to: UInt8.self) else {
return QPQ_ERROR
}
return qpq_send(h, recipient, ptr, buffer.count)
}
try checkStatus(code, handle: h)
}
}
/// Receive pending messages, blocking up to ``timeoutMs`` milliseconds.
///
/// - Returns: An array of message strings (UTF-8).
public func receive(timeoutMs: UInt32 = 5000) throws -> [String] {
try withHandle { h in
var jsonPtr: UnsafeMutablePointer<CChar>?
let code = qpq_receive(h, timeoutMs, &jsonPtr)
try checkStatus(code, handle: h)
guard let jsonPtr else {
return []
}
defer { qpq_free_string(jsonPtr) }
let jsonString = String(cString: jsonPtr)
guard let data = jsonString.data(using: .utf8),
let array = try? JSONSerialization.jsonObject(with: data) as? [String]
else {
return []
}
return array
}
}
// MARK: - Lifecycle
/// Disconnect from the server and release resources.
public func disconnect() {
lock.lock()
defer { lock.unlock() }
if let h = handle {
qpq_disconnect(h)
handle = nil
}
}
/// Whether the client is currently connected.
public var isConnected: Bool {
lock.lock()
defer { lock.unlock() }
return handle != nil
}
// MARK: - Internal
private func withHandle<T>(_ body: (OpaquePointer) throws -> T) throws -> T {
lock.lock()
defer { lock.unlock() }
guard let h = handle else {
throw QpqError.notConnected
}
return try body(h)
}
private func checkStatus(_ code: Int32, handle: OpaquePointer) throws {
guard code != QPQ_OK else { return }
let msg: String
if let errPtr = qpq_last_error(handle) {
msg = String(cString: errPtr)
} else {
msg = "unknown error"
}
switch code {
case QPQ_AUTH_FAILED:
throw QpqError.authFailed(msg)
case QPQ_TIMEOUT:
throw QpqError.timeout(msg)
case QPQ_NOT_CONNECTED:
throw QpqError.notConnected
default:
throw QpqError.ffiError(msg)
}
}
}