ruvector/crates/ruvector-kalshi/examples/validate.rs
ruvnet ff0f5bc4fa feat(kalshi): ruvector-kalshi + neural-trader-strategies (ADR-151)
New crate ruvector-kalshi: RSA-PSS-SHA256 signer (PKCS#1/#8), GCS/local/env
secret loader with 5-min cache, typed REST + WS DTOs, Kalshi→MarketEvent
normalizer (reuses neural-trader-core), transport-free FeedDecoder,
reqwest-backed REST client with live-trade env gate, and an offline
sign+verify example that validates against the real PEM.

New crate neural-trader-strategies: venue-agnostic Strategy trait, Intent
type, RiskGate (position cap, daily-loss kill, concentration, min-edge,
live gate, cash check), and ExpectedValueKelly prior-driven strategy.

36 unit tests pass across both crates. End-to-end offline validation
confirmed against the real Kalshi PEM via both local and GCS sources.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-04-20 15:25:01 -04:00

70 lines
2.1 KiB
Rust

//! Offline validator: loads the real Kalshi credentials via `SecretLoader`,
//! signs a sample request, verifies the signature round-trips.
//!
//! No network calls are made. This proves the actual PEM on disk (or in
//! GCS) is loadable by this crate and produces valid RSA-PSS-SHA256
//! signatures.
//!
//! Run:
//! # Offline, local PEM:
//! KALSHI_SECRET_SOURCE=local cargo run -p ruvector-kalshi --example validate
//!
//! # From Google Cloud Secret Manager (needs `gcloud` auth):
//! cargo run -p ruvector-kalshi --example validate
use ruvector_kalshi::{
auth::{self, Signer},
secrets::SecretLoader,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let project = std::env::var("KALSHI_GCP_PROJECT").unwrap_or_else(|_| "ruv-dev".into());
let loader = SecretLoader::new(project);
let creds = loader.load().await?;
println!("loaded credentials: {creds:?}");
let signer = Signer::from_pem(&creds.api_key, &creds.private_key_pem)?;
println!("signer ready: {signer:?}");
let ts_ms = 1_700_000_000_000u64;
let method = "GET";
let path = "/trade-api/v2/exchange/status";
let headers = signer.sign_with_ts(ts_ms, method, path);
println!(
"signed (ts={}, method={method}, path={path}):\n \
KALSHI-ACCESS-KEY: <len {} chars>\n \
KALSHI-ACCESS-TIMESTAMP: {}\n \
KALSHI-ACCESS-SIGNATURE: <len {} chars, base64>",
ts_ms,
headers.access_key.len(),
headers.timestamp_ms,
headers.signature_b64.len(),
);
auth::verify(
&signer.verifying_key(),
ts_ms,
method,
path,
&headers.signature_b64,
)?;
println!("signature verified against derived public key — OK");
// Tampered verification must fail.
let tamper = auth::verify(
&signer.verifying_key(),
ts_ms,
method,
"/trade-api/v2/portfolio",
&headers.signature_b64,
);
if tamper.is_ok() {
anyhow::bail!("tampered path unexpectedly verified — signing is broken");
}
println!("tamper check rejected — OK");
println!("\nall offline checks passed.");
Ok(())
}