Rust SDK
Single-file Rust client — ureq + serde. Cargo-ready.
Download
zeq_client.rs online · v1.1.3ureq + serde
Single-file Rust client using ureq (tiny blocking HTTP) and serde. Cargo-ready; three short dependencies.
Contract-parity note: Same v1.1.3 wire format as the Python and
JavaScript clients (both smoke-tested live, NM21 → 1.98049e20 N). The Rust client has not
been compiled inside our build sandbox because no Rust toolchain is available there. File issues for any cargo/rustc
problems — fixes ship same day.
Cargo.toml
[package]
name = "zeq_client_demo"
version = "0.1.0"
edition = "2021"
[dependencies]
ureq = { version = "2", features = ["json"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Quickstart
use serde_json::json;
mod zeq_client;
use zeq_client::Client;
fn main() {
let c = Client::new(""); // reads ZEQ_TOKEN env
let r = c.compute("NM21", json!({
"m1": 5.972e24_f64, "m2": 7.342e22_f64, "r": 3.844e8_f64
})).unwrap();
println!("{:?} {:?}", r.value, r.unit); // → Some(1.98049e20) Some("N")
}
Full source — zeq_client.rs
Why inline? The full SDK source is pasted below so the mathematics is auditable.
Copy-paste it, read it, self-host it — you never have to trust the Zeq API as a black box.
Use our key for convenience, or wire it into your own backend. The v1.1.3 binding contract and ZeqProof HMAC are identical either way.
zeq_client.rs 5730 bytes 156 lines Rust 2021
//! zeq_client.rs — single-file Rust SDK for the Zeq HTTP API (v1.1.3 contract).
//!
//! Zero third-party dependencies beyond the Rust standard library and `ureq`
//! (a tiny blocking HTTP client, ~500 KB with rustls). Runs out of the box:
//!
//! ```toml
//! # Cargo.toml
//! [dependencies]
//! ureq = { version = "2", features = ["json"] }
//! serde = { version = "1", features = ["derive"] }
//! serde_json = "1"
//! ```
//!
//! ```bash
//! ZEQ_TOKEN=zeq_ak_... cargo run --example zeq_client
//! ```
//!
//! Mirrors `zeq_client.py`, `zeq_client.js`, and `zeq_client.go`:
//!
//! - `POST /api/zeq/compute { operator_id, inputs }` → numeric result
//! - `POST /api/zeq/prove { operator_id, inputs }` → ZeqProof HMAC
//!
//! [Zeq Daemon] HulyaPulse 1.287 Hz — τ_zeqond = 0.777 s — α_K = 0.00129
use serde::Deserialize;
use serde_json::{json, Value};
use std::{env, fmt};
pub const ALPHA_SPEC: f64 = 0.00129;
pub const F_HULYA: f64 = 1.287;
pub const TAU_ZEQOND: f64 = 0.777;
#[derive(Debug)]
pub enum ZeqError {
MissingToken,
Http { status: u16, code: String, message: String, body: String },
Transport(String),
Decode(String),
}
impl fmt::Display for ZeqError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ZeqError::MissingToken => write!(f, "zeq: missing token (set ZEQ_TOKEN or pass to Client::new)"),
ZeqError::Http { status, code, message, .. } => write!(f, "zeq: {message} (status={status} code={code})"),
ZeqError::Transport(e) => write!(f, "zeq: transport error: {e}"),
ZeqError::Decode(e) => write!(f, "zeq: decode error: {e}"),
}
}
}
impl std::error::Error for ZeqError {}
#[derive(Debug, Deserialize)]
pub struct ComputeResult {
pub value: Option<f64>,
pub unit: Option<String>,
pub uncertainty: Option<f64>,
pub operator_id: Option<String>,
pub solver: Option<String>,
pub binding_overlay: Option<String>,
#[serde(rename = "zeqProof")]
pub zeq_proof: Option<String>,
#[serde(skip)]
pub raw: Value,
}
pub struct Client {
pub token: String,
pub base_url: String,
}
impl Client {
pub fn new(token: impl Into<String>) -> Self {
let mut t = token.into();
if t.is_empty() { t = env::var("ZEQ_TOKEN").unwrap_or_default(); }
Self { token: t, base_url: "https://www.zeq.dev".into() }
}
fn post(&self, path: &str, body: Value) -> Result<Value, ZeqError> {
if self.token.is_empty() { return Err(ZeqError::MissingToken); }
let url = format!("{}{}", self.base_url.trim_end_matches('/'), path);
let resp = ureq::post(&url)
.set("Content-Type", "application/json")
.set("Authorization", &format!("Bearer {}", self.token))
.send_json(body);
match resp {
Ok(r) => r.into_json::<Value>().map_err(|e| ZeqError::Decode(e.to_string())),
Err(ureq::Error::Status(code, r)) => {
let body = r.into_string().unwrap_or_default();
let parsed: Value = serde_json::from_str(&body).unwrap_or(Value::Null);
let msg = parsed.get("error").and_then(|e| e.get("message"))
.and_then(|s| s.as_str()).unwrap_or("HTTP error").to_string();
let errcode = parsed.get("error").and_then(|e| e.get("code"))
.and_then(|s| s.as_str()).unwrap_or("HTTP_ERROR").to_string();
Err(ZeqError::Http { status: code, code: errcode, message: msg, body })
}
Err(e) => Err(ZeqError::Transport(e.to_string())),
}
}
pub fn compute(&self, operator_id: &str, inputs: Value) -> Result<ComputeResult, ZeqError> {
let raw = self.post("/api/zeq/compute", json!({
"operator_id": operator_id,
"inputs": inputs,
}))?;
Ok(unpack(raw))
}
pub fn prove(&self, operator_id: &str, inputs: Value) -> Result<ComputeResult, ZeqError> {
let raw = self.post("/api/zeq/prove", json!({
"operator_id": operator_id,
"inputs": inputs,
}))?;
Ok(unpack(raw))
}
}
fn unpack(raw: Value) -> ComputeResult {
let mut r: ComputeResult = serde_json::from_value(raw.clone()).unwrap_or(ComputeResult {
value: None, unit: None, uncertainty: None, operator_id: None,
solver: None, binding_overlay: None, zeq_proof: None, raw: Value::Null,
});
r.raw = raw;
r
}
// ── CLI smoke test ────────────────────────────────────────────────────────
// This is gated behind `fn main()` so a single `rustc zeq_client.rs` (with the
// three crates above in scope) runs the Earth-Moon gravity test.
fn main() {
let c = Client::new("");
let res = c.compute("NM21", json!({
"m1": 5.972e24f64, "m2": 7.342e22f64, "r": 3.844e8f64
}));
match res {
Ok(r) => {
println!("{}", "=".repeat(70));
println!("NM21 — Earth-Moon gravitational force (expected ~ 1.98e20 N)");
println!("{}", "=".repeat(70));
println!("value = {:?}", r.value);
println!("unit = {:?}", r.unit);
println!("uncertainty = {:?}", r.uncertainty);
println!("solver = {:?}", r.solver);
println!("binding_overlay = {:?}", r.binding_overlay);
let proof = r.zeq_proof.unwrap_or_default();
let preview: String = proof.chars().take(24).collect();
println!("zeqProof = {preview}...");
}
Err(e) => {
eprintln!("error: {e}");
std::process::exit(1);
}
}
}