geronimo-captcha

Secure, AI-resistant, JavaScript-free CAPTCHA built in Rust.
Confuses bots, but delights humans.
What it does
- Renders a 3×3 sprite with one correctly oriented tile
- Random jitter, label offset, colored noise, JPEG artifacts
- Stateless HMAC-signed challenge id with TTL
Challenge examples
Todos
Quick start
Generate
use std::sync::Arc;
use geronimo_captcha::{
CaptchaManager, ChallengeInMemoryRegistry, GenerationOptions, NoiseOptions,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let secret = "your-secret-key".to_string();
let ttl_secs = 60;
let noise = NoiseOptions::default();
let gen = GenerationOptions {
cell_size: 150,
jpeg_quality: 20,
limits: None,
};
let registry = Arc::new(ChallengeInMemoryRegistry::new(ttl_secs, 3));
let mgr = CaptchaManager::new(secret, ttl_secs, noise, Some(registry), gen);
let challenge = mgr.generate_challenge()?;
let img_src = challenge.sprite_uri; let challenge_id = challenge.challenge_id;
println!("img_src prefix: {}", &img_src[..32.min(img_src.len())]);
println!("challenge_id: {}", challenge_id);
Ok(())
}
Verify
use geronimo_captcha::{CaptchaManager, GenerationOptions, NoiseOptions};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let secret = "your-secret-key".to_string();
let ttl_secs = 60;
let noise = NoiseOptions::default();
let gen = GenerationOptions {
cell_size: 150,
jpeg_quality: 20,
limits: None,
};
let mgr = CaptchaManager::new(secret, ttl_secs, noise, None, gen);
let challenge_id = "nonce:1730534400:BASE64_HMAC".to_string();
let user_choice: u8 = 7;
let ok = mgr.verify_challenge(&challenge_id, user_choice)?;
println!("verified: {ok}");
Ok(())
}
License
This project is licensed under the Apache 2.0 License. See LICENSE for details.