# agentctl-auth
Unified auth pool and Claude API client for managing multiple AI provider credentials with automatic token rotation.
## Features
- **Auth Pool**: Manage credentials for multiple providers in a single TOML file
- **Token Rotation**: Automatic rotation on 429 rate limits (supports multiple tokens per provider)
- **Claude Client**: Messages API with OAuth stealth headers for Claude Max Plan integration
- **Credential Testing**: Validate API keys against provider endpoints
- **Import**: Import from auth-profiles.json format (OpenClaw/RustClaw compatible)
- **Usage Tracking**: Record success/failure, cooldowns, and last-used timestamps
## Quick Start
```rust
use agentctl_auth::{AuthPool, claude};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load auth pool
let pool = AuthPool::load(&std::path::PathBuf::from("~/.agentctl/auth.toml"))?;
// Build Claude client with automatic token rotation
let client = claude::Client::builder()
.pool(&pool)
.build()?;
// Make a request
let response = client.message(
"claude-sonnet-4-20250514",
&[claude::Message::user("Hello!")],
4096,
).await?;
println!("Response: {}", response.content[0].text);
println!("Tokens: {} in + {} out", response.usage.input_tokens, response.usage.output_tokens);
Ok(())
}
```
## Auth Pool Format
```toml
[pool]
"anthropic:default" = { provider = "anthropic", type = "token", token = "sk-ant-..." }
"anthropic:backup" = { provider = "anthropic", type = "oauth", token = "sk-ant-..." }
[defaults]
anthropic = "anthropic:default"
[order.anthropic]
0 = "anthropic:default"
1 = "anthropic:backup"
```
## Claude Client Features
- **Stealth Headers**: Mimics Claude Code CLI for Max Plan access
- **Auto-rotation**: On 429, switches to next credential in order
- **Cooldown Management**: Skips credentials in cooldown period
- **Usage Tracking**: Records success/failure for each credential
## Testing Credentials
```rust
use agentctl_auth::{test_credential, test_all};
let pool = AuthPool::load(&path)?;
// Test single credential
let result = test_credential("anthropic:default", pool.get("anthropic:default").unwrap()).await;
println!("Valid: {}, Latency: {}ms", result.success, result.latency_ms);
// Test all credentials
let results = test_all(&pool).await;
```
## Import from auth-profiles.json
```rust
let mut pool = AuthPool::default();
pool.import_from_auth_profiles_file(&path)?;
pool.save(&std::path::PathBuf::from("~/.agentctl/auth.toml"))?;
```
## License
Dual-licensed under MIT or Apache-2.0.