{"id":47809720,"url":"https://github.com/godaddy/ans-sdk-rust","last_synced_at":"2026-04-03T18:05:22.328Z","repository":{"id":341866553,"uuid":"1171093011","full_name":"godaddy/ans-sdk-rust","owner":"godaddy","description":"Agent Name Service SDK written in rust.","archived":false,"fork":false,"pushed_at":"2026-03-25T20:30:57.000Z","size":519,"stargazers_count":4,"open_issues_count":6,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T18:39:12.599Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/godaddy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-02T21:38:05.000Z","updated_at":"2026-03-16T09:44:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/godaddy/ans-sdk-rust","commit_stats":null,"previous_names":["godaddy/ans-sdk-rust"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/godaddy/ans-sdk-rust","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fans-sdk-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fans-sdk-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fans-sdk-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fans-sdk-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/godaddy","download_url":"https://codeload.github.com/godaddy/ans-sdk-rust/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/godaddy%2Fans-sdk-rust/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31368157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-03T17:53:18.093Z","status":"ssl_error","status_checked_at":"2026-04-03T17:53:17.617Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-03T18:05:22.157Z","updated_at":"2026-04-03T18:05:22.320Z","avatar_url":"https://github.com/godaddy.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ANS Rust Libraries\n\nRust libraries for the Agent Name Service (ANS) ecosystem.\n\n## Crates\n\n| Crate | Description | Status |\n|-------|-------------|--------|\n| [`ans-types`](crates/ans-types) | Shared types for ANS (Badge, Fqdn, AnsName, etc.) | Ready |\n| [`ans-verify`](crates/ans-verify) | Trust verification library | Ready |\n| [`ans-client`](crates/ans-client) | ANS API client for registration | Ready |\n\n## Overview\n\nThe ANS architecture uses a dual-certificate model:\n\n| Certificate Type | Issuer | Contains | Purpose |\n|-----------------|--------|----------|---------|\n| Public Server Certificate | Public CA (e.g., Let's Encrypt) | FQDN in SAN | Server TLS identity |\n| Private Identity Certificate | ANS Private CA | FQDN as CN, ANSName as URI SAN | Agent identity for mTLS |\n\nVerification relies on:\n- **DNS `_ans-badge` TXT records** pointing to the transparency log (with `_ra-badge` fallback)\n- **Transparency Log API** returning badges with status and certificate fingerprints\n- **Certificate fingerprint comparison** to ensure the presented certificate matches the registered identity\n- **DANE/TLSA records** (optional) for additional certificate binding via DNSSEC\n\n## Installation\n\nAdd to your `Cargo.toml`:\n\n```toml\n[dependencies]\n# For verification\nans-verify = { git = \"https://github.com/godaddy/ans-sdk-rust\" }\n\n# For API client\nans-client = { git = \"https://github.com/godaddy/ans-sdk-rust\" }\n\n# For shared types only\nans-types = { git = \"https://github.com/godaddy/ans-sdk-rust\" }\n\ntokio = { version = \"1\", features = [\"rt-multi-thread\", \"macros\"] }\n```\n\n## API Client Quick Start\n\n```rust\nuse ans_client::{AnsClient, models::*};\n\n#[tokio::main]\nasync fn main() -\u003e ans_client::Result\u003c()\u003e {\n    // Create client with JWT authentication\n    let client = AnsClient::builder()\n        .base_url(\"https://api.godaddy.com\")\n        .jwt(\"your-jwt-token\")\n        .build()?;\n\n    // Search for agents\n    let mut criteria = SearchCriteria::default();\n    criteria.agent_host = Some(\"example.com\".into());\n    let results = client.search_agents(\u0026criteria, Some(10), None).await?;\n\n    for agent in results.agents {\n        println!(\"{}: {}\", agent.ans_name, agent.agent_display_name);\n    }\n\n    Ok(())\n}\n```\n\n### Registration Flow\n\n```rust\nuse ans_client::{AnsClient, models::*};\n\n#[tokio::main]\nasync fn main() -\u003e ans_client::Result\u003c()\u003e {\n    let client = AnsClient::builder()\n        .base_url(\"https://api.godaddy.com\")\n        .jwt(\"your-jwt-token\")\n        .build()?;\n\n    // Step 1: Register agent\n    let endpoint = AgentEndpoint::new(\"https://agent.example.com/mcp\", Protocol::Mcp)\n        .with_transports(vec![Transport::StreamableHttp]);\n\n    let request = AgentRegistrationRequest::new(\n        \"my-agent\",\n        \"agent.example.com\",\n        \"1.0.0\",\n        std::fs::read_to_string(\"agent.example.com/identity_v1.0.0.csr\")?,\n        vec![endpoint],\n    )\n    .with_description(\"My AI agent\")\n    .with_server_csr_pem(std::fs::read_to_string(\"agent.example.com/server_v1.0.0.csr\")?);\n\n    let pending = client.register_agent(\u0026request).await?;\n    println!(\"Agent ID: {:?}\", pending.agent_id);\n    println!(\"Next steps: {:?}\", pending.next_steps);\n\n    // Step 2: Configure ACME challenge from pending.challenges\n    // ... set up DNS-01 or HTTP-01 challenge ...\n\n    // Step 3: Verify domain ownership\n    let agent_id = pending.agent_id.unwrap();\n    let status = client.verify_acme(\u0026agent_id).await?;\n\n    // Step 4: Configure DNS records from pending.dns_records\n    // ... set up _ans-badge TXT record, etc. ...\n\n    // Step 5: Verify DNS configuration\n    let status = client.verify_dns(\u0026agent_id).await?;\n    println!(\"Final status: {:?}\", status.status);\n\n    Ok(())\n}\n```\n\n### Authentication Methods\n\n```rust\n// JWT authentication\nlet client = AnsClient::builder()\n    .base_url(\"https://api.godaddy.com\")\n    .jwt(\"your-jwt-token\")\n    .build()?;\n\n// API key authentication\nlet client = AnsClient::builder()\n    .base_url(\"https://api.godaddy.com\")\n    .api_key(\"your-key\", \"your-secret\")\n    .build()?;\n```\n\n## Verification Quick Start\n\n### Server Verification (Client verifying Server)\n\n```rust\nuse ans_verify::{AnsVerifier, CertFingerprint, CertIdentity, VerificationOutcome};\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let verifier = AnsVerifier::builder()\n        .with_caching()\n        .build()\n        .await?;\n\n    // After TLS handshake, extract server certificate info\n    let server_cert = CertIdentity::new(\n        Some(\"agent.example.com\".to_string()),\n        vec![\"agent.example.com\".to_string()],\n        vec![],\n        CertFingerprint::from_der(\u0026cert_der_bytes),\n    );\n\n    match verifier.verify_server(\"agent.example.com\", \u0026server_cert).await {\n        VerificationOutcome::Verified { badge, .. } =\u003e {\n            println!(\"Verified ANS agent: {}\", badge.agent_name());\n        }\n        VerificationOutcome::NotAnsAgent { fqdn } =\u003e {\n            println!(\"Not a registered ANS agent: {}\", fqdn);\n        }\n        outcome =\u003e {\n            println!(\"Verification failed: {:?}\", outcome);\n        }\n    }\n\n    Ok(())\n}\n```\n\n### Client Verification (Server verifying mTLS Client)\n\n```rust\nuse ans_verify::{AnsVerifier, CertFingerprint, CertIdentity, VerificationOutcome};\n\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let verifier = AnsVerifier::builder()\n        .with_caching()\n        .build()\n        .await?;\n\n    // After mTLS handshake, extract client certificate info\n    // The identity cert must contain URI SAN with ANS name (ans://v1.0.0.agent.example.com)\n    let client_cert = CertIdentity::new(\n        Some(\"agent.example.com\".to_string()),\n        vec![\"agent.example.com\".to_string()],\n        vec![\"ans://v1.0.0.agent.example.com\".to_string()],\n        CertFingerprint::from_der(\u0026cert_der_bytes),\n    );\n\n    match verifier.verify_client(\u0026client_cert).await {\n        VerificationOutcome::Verified { badge, .. } =\u003e {\n            println!(\"Verified ANS agent: {}\", badge.agent_name());\n            // Process requests from this client\n        }\n        outcome =\u003e {\n            println!(\"Verification failed: {:?}\", outcome);\n            // Reject connection\n        }\n    }\n\n    Ok(())\n}\n```\n\n## Configuration\n\n### Verifier Builder Options\n\n```rust\nlet verifier = AnsVerifier::builder()\n    // Enable badge caching (recommended)\n    .with_caching()\n\n    // Or with custom cache configuration\n    .with_cache_config(CacheConfig {\n        max_entries: 1000,\n        default_ttl: Duration::from_secs(300),\n        refresh_threshold: Duration::from_secs(60),\n    })\n\n    // Set failure policy\n    .failure_policy(FailurePolicy::FailClosed)  // Default: reject on any error\n    // Or: FailurePolicy::FailOpenWithCache { max_staleness: Duration::from_secs(600) }\n\n    // Custom DNS resolver (for testing or special configurations)\n    .dns_resolver(Arc::new(custom_resolver))\n\n    // Custom transparency log client\n    .tlog_client(Arc::new(custom_client))\n\n    // DANE/TLSA verification (optional)\n    .dane_policy(DanePolicy::ValidateIfPresent)  // Check TLSA if present\n    // Or: .require_dane()  // Fail if no TLSA records\n    // Or: .with_dane_if_present()  // Shorthand for ValidateIfPresent\n    .dane_port(443)  // Port for TLSA lookup (default: 443)\n\n    // Trusted RA domains (optional, defense-in-depth)\n    .trusted_ra_domains([\"tlog.example.com\", \"tlog2.example.com\"])\n\n    .build()\n    .await?;\n```\n\n### Failure Policies\n\n| Policy | Behavior | Use Case |\n|--------|----------|----------|\n| `FailClosed` | Reject on any error | High security (default) |\n| `FailOpenWithCache` | Use cached badge if fresh enough | Balance availability/security |\n\n### DANE/TLSA Policies\n\nDANE binds certificates to DNS names via TLSA records, providing additional verification when DNSSEC is enabled.\n\n| Policy | Behavior | Use Case |\n|--------|----------|----------|\n| `Disabled` | Skip TLSA verification | Default, no DANE overhead |\n| `ValidateIfPresent` | Verify TLSA if records exist, skip if not | Opportunistic security |\n| `Required` | Require TLSA records to exist and match | High security with DNSSEC |\n\n### DNS Resolver Configuration\n\n```rust\nlet verifier = AnsVerifier::builder()\n    // Use Cloudflare DNS\n    .dns_cloudflare()\n\n    // Or Cloudflare DNS-over-TLS\n    .dns_cloudflare_tls()\n\n    // Or Google Public DNS\n    .dns_google()\n\n    // Or Quad9 (includes malware blocking)\n    .dns_quad9()\n\n    // Or custom nameservers\n    .dns_nameservers(\u0026[\n        Ipv4Addr::new(1, 1, 1, 1),\n        Ipv4Addr::new(8, 8, 8, 8),\n    ])\n\n    .build()\n    .await?;\n```\n\n| Preset | Servers | Features |\n|--------|---------|----------|\n| `dns_cloudflare()` | 1.1.1.1, 1.0.0.1 | Fast, privacy-focused |\n| `dns_cloudflare_tls()` | 1.1.1.1 (DoT) | Encrypted queries |\n| `dns_google()` | 8.8.8.8, 8.8.4.4 | Reliable, global |\n| `dns_google_tls()` | 8.8.8.8 (DoT) | Encrypted queries |\n| `dns_quad9()` | 9.9.9.9 | Malware blocking |\n\n## Verification Outcomes\n\n| Outcome | Meaning |\n|---------|---------|\n| `Verified` | Certificate matches registered ANS agent |\n| `NotAnsAgent` | No `_ans-badge` or `_ra-badge` DNS record found |\n| `InvalidStatus` | Badge status is `EXPIRED` or `REVOKED` |\n| `FingerprintMismatch` | Certificate fingerprint doesn't match badge |\n| `HostnameMismatch` | Certificate CN doesn't match badge agent.host |\n| `AnsNameMismatch` | URI SAN doesn't match badge ansName (mTLS only) |\n| `DnsError` | DNS lookup failed |\n| `TlogError` | Transparency log API error |\n| `DaneError` | DANE/TLSA verification failed |\n| `CertError` | Certificate parsing failure |\n| `ParseError` | FQDN or AnsName parse failure |\n\n## Badge Status Values\n\n| Status | Valid for Connections | Description |\n|--------|----------------------|-------------|\n| `Active` | Yes | Agent is registered and in good standing |\n| `Warning` | Yes | Certificate expires within 30 days |\n| `Deprecated` | Yes | AHP has marked this version for retirement; consumers should migrate |\n| `Expired` | No | Certificate has expired |\n| `Revoked` | No | Registration has been explicitly revoked |\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        AnsVerifier                               │\n│  ┌─────────────────────┐     ┌─────────────────────┐            │\n│  │   ServerVerifier    │     │   ClientVerifier    │            │\n│  │  (client-side TLS)  │     │  (server-side mTLS) │            │\n│  │  + DANE/TLSA verify │     │                     │            │\n│  └──────────┬──────────┘     └──────────┬──────────┘            │\n└─────────────┼───────────────────────────┼───────────────────────┘\n              │                           │\n              ▼                           ▼\n┌─────────────────────────────────────────────────────────────────┐\n│                         BadgeCache                               │\n│                    (TTL-based caching)                          │\n└─────────────────────────────────────────────────────────────────┘\n              │                           │\n              ▼                           ▼\n┌──────────────────────┐     ┌────────────────────────────────────┐\n│     DnsResolver      │     │    TransparencyLogClient           │\n│ (_ans-badge lookup)  │     │      (badge API)                   │\n│  (TLSA lookup)       │     │                                    │\n└──────────────────────┘     └────────────────────────────────────┘\n\n┌─────────────────────────────────────────────────────────────────┐\n│                        AnsClient                                 │\n│  ┌─────────────────────────────────────────────────────────────┐│\n│  │  Registration │ Discovery │ Certificates │ Revocation       ││\n│  └─────────────────────────────────────────────────────────────┘│\n│                              │                                   │\n│                              ▼                                   │\n│  ┌─────────────────────────────────────────────────────────────┐│\n│  │              ANS Registry API (HTTP/JSON)                   ││\n│  └─────────────────────────────────────────────────────────────┘│\n└─────────────────────────────────────────────────────────────────┘\n```\n\n## Testing\n\nThe libraries include mock implementations behind the `test-support` feature flag:\n\n```toml\n[dev-dependencies]\nans-verify = { ..., features = [\"test-support\"] }\n```\n\n```rust\nuse ans_verify::{MockDnsResolver, MockTransparencyLogClient, TlsaRecord};\n\nlet dns_resolver = Arc::new(\n    MockDnsResolver::new()\n        .with_records(\"agent.example.com\", vec![badge_record])\n        .with_tlsa_records(\"agent.example.com\", 443, vec![tlsa_record])\n);\n\nlet tlog_client = Arc::new(\n    MockTransparencyLogClient::new()\n        .with_badge(\"https://tlog.example.com/badge\", badge)\n);\n\nlet verifier = ServerVerifier::builder()\n    .dns_resolver(dns_resolver)\n    .tlog_client(tlog_client)\n    .with_dane_if_present()\n    .build()\n    .await?;\n```\n\nRun tests:\n```bash\ncargo test --workspace --features ans-verify/test-support\n```\n\n## Logging\n\nThe libraries use the `tracing` crate for structured logging:\n\n```rust\nuse tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};\n\ntracing_subscriber::registry()\n    .with(tracing_subscriber::EnvFilter::try_from_default_env()\n        .unwrap_or_else(|_| \"ans_verify=info\".into()))\n    .with(tracing_subscriber::fmt::layer())\n    .init();\n```\n\nRun with environment variable for different log levels:\n```bash\nRUST_LOG=ans_verify=debug cargo run  # Detailed verification steps\nRUST_LOG=ans_client=debug cargo run  # API request/response details\n```\n\n## TLS Integration (rustls)\n\nThe `ans-verify` crate provides optional rustls integration for verifying certificates during TLS handshakes.\n\nEnable the feature:\n```toml\n[dependencies]\nans-verify = { ..., features = [\"rustls\"] }\n```\n\n### Server Certificate Verification (Client-side)\n\nUse `AnsServerCertVerifier` to verify server certificates match the ANS badge during the TLS handshake:\n\n```rust\nuse ans_verify::{AnsVerifier, AnsServerCertVerifier, CertFingerprint, DanePolicy};\nuse std::sync::Arc;\n\n// Pre-fetch the badge to get expected fingerprint\nlet verifier = AnsVerifier::builder()\n    .dane_policy(DanePolicy::ValidateIfPresent)\n    .with_caching()\n    .build()\n    .await?;\n\nlet badge = verifier.prefetch(\"agent.example.com\").await?;\nlet expected_fp = CertFingerprint::parse(badge.server_cert_fingerprint())?;\n\n// Create TLS config with ANS verification\nlet server_verifier = AnsServerCertVerifier::new(expected_fp)?;\n\nlet tls_config = rustls::ClientConfig::builder()\n    .dangerous()\n    .with_custom_certificate_verifier(Arc::new(server_verifier))\n    .with_no_client_auth();\n```\n\n### Client Certificate Verification (Server-side mTLS)\n\nUse `AnsClientCertVerifier` for the TLS handshake (validates chain to Private CA), then verify against the badge post-handshake:\n\n```rust\nuse ans_verify::{AnsClientCertVerifier, AnsVerifier, CertIdentity, VerificationOutcome};\nuse std::sync::Arc;\n\n// Load Private CA for TLS handshake validation\nlet client_verifier = AnsClientCertVerifier::from_pem(\u0026ca_pem)?;\n\nlet server_config = rustls::ServerConfig::builder()\n    .with_client_cert_verifier(Arc::new(client_verifier))\n    .with_single_cert(server_certs, server_key)?;\n\n// After TLS handshake, verify client against badge\nlet verifier = AnsVerifier::builder().with_caching().build().await?;\n\n// Extract client cert identity from the TLS connection\nlet cert_identity = CertIdentity::from_der(client_cert_der)?;\n\nmatch verifier.verify_client(\u0026cert_identity).await {\n    VerificationOutcome::Verified { badge, .. } =\u003e {\n        println!(\"Verified ANS agent: {}\", badge.agent_name());\n    }\n    outcome =\u003e {\n        println!(\"Verification failed: {:?}\", outcome);\n    }\n}\n```\n\n## Examples\n\nSee the `crates/ans-verify/examples/` directory:\n\n| Example | Description | Features |\n|---------|-------------|----------|\n| `verify_server.rs` | Server verification flow | - |\n| `verify_mtls_client.rs` | mTLS client verification flow | - |\n| `gen_test_certs.rs` | Generate CA, server, and client certificates | - |\n| `local_mtls.rs` | Self-contained mTLS demo (generates certs in-memory) | `rustls`, `test-support` |\n| `mcp_mtls_client.rs` | Connect to real MCP server with ANS verification | `rustls` |\n\n### Generate Test Certificates\n\n```bash\ncargo run -p ans-verify --example gen_test_certs -- --output-dir ./test-certs\n```\n\n### Run Local mTLS Demo\n\nThis self-contained example generates certificates in-memory, then runs a TLS server and client with mock DNS and transparency log:\n\n```bash\ncargo run -p ans-verify --example local_mtls --features \"rustls,test-support\"\n```\n\n### Connect to Real MCP Server\n\nRequires ANS identity certificates issued by the Private CA:\n\n```bash\nANS_CERT_PATH=/path/to/identity.crt \\\nANS_KEY_PATH=/path/to/identity.key \\\nANS_SERVER_URL=https://agent.example.com/mcp \\\ncargo run -p ans-verify --example mcp_mtls_client --features rustls\n```\n\n### Basic Verification Examples\n\n```bash\nRUST_LOG=ans_verify=debug cargo run -p ans-verify --example verify_server\nRUST_LOG=ans_verify=debug cargo run -p ans-verify --example verify_mtls_client\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Fans-sdk-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgodaddy%2Fans-sdk-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgodaddy%2Fans-sdk-rust/lists"}