Shodan API client adapter — host/service/cert/vuln lookups, API-key auth
| Tier | infrastructure |
| Role | unclassified (baselined) |
| Path | crates/infrastructure/adapters-shodan |
| Edition | 2021 |
| Targets | infrastructure_adapters_shodan |
| Public items | 0 across 0 modules |
| Tests | 26 |
What it is for
Shodan API client adapter.
A pure API-client adapter over Shodan's host/DNS/vulnerability lookup endpoints (https://api.shodan.io). API-key auth only — the key rides as a key query parameter on every request, never as a header or bearer token (that's Shodan's own scheme, not a choice made here).
Lifted from plumber/src/collectors/shodan.rs (Gate 4.5 provenance, see CHANGELOG.md). Unlike four of its five sibling files in that source directory, shodan.rs never implemented plumber's Collector trait (renamed SourceIngest on this harvest, foundation-source-ingest) — it has no collect() -> CollectionResult-shaped method, only typed host/DNS/vuln lookups. This crate stays a pure client for the same reason: any SourceIngest implementation (mapping a ShodanHost into whatever domain result type a consumer wants) is a separate, higher-layer "Shodan collector" piece a later sprint can build on top of this one — see CHANGELOG.md for the full reasoning.
# Example
# async fn example() -> Result<(), infrastructure_adapters_shodan::ShodanError> {
let client = infrastructure_adapters_shodan::ShodanClient::from_api_key("YOUR_API_KEY")?;
let host = client.host("8.8.8.8").await?;
println!("{:?}", host.ports);
# Ok(())
# }
Capabilities
No public items.
How to use it
From this crate's own rustdoc:
# async fn example() -> Result<(), infrastructure_adapters_shodan::ShodanError> {
let client = infrastructure_adapters_shodan::ShodanClient::from_api_key("YOUR_API_KEY")?;
let host = client.host("8.8.8.8").await?;
println!("{:?}", host.ports);
# Ok(())
# }
Module structure
No public modules: the crate root is its whole surface.
Public surface
No public items.
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
ShodanClient | client::ShodanClient |
ShodanConfig | auth::ShodanConfig |
ShodanError | error::ShodanError |
{ShodanApiError,ShodanApiInfo,ShodanCert,ShodanCipher,ShodanDnsDomain,ShodanDnsRecord,ShodanHost,ShodanHttp,ShodanRedirect,ShodanService,ShodanSsl,ShodanVuln,} | types::{ShodanApiError,ShodanApiInfo,ShodanCert,ShodanCipher,ShodanDnsDomain,ShodanDnsRecord,ShodanHost,ShodanHttp,ShodanRedirect,ShodanService,ShodanSsl,ShodanVuln,} |
Boundary
Depends on no other workspace tier.
Shares tier infrastructure with 82 other crates: infrastructure-acquire, infrastructure-adapters-google-calendar, infrastructure-adapters-google-gmail, infrastructure-adapters-google-places, infrastructure-adapters-google-trends, infrastructure-adapters-yelp, infrastructure-agent, infrastructure-ai, … (82 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | infrastructure |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/infrastructure/adapters-shodan |
| Vocabulary in force (lexicon) | current |
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `infrastructure-fetcher` | infrastructure | yes | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
reqwest | ^0.12 | json | no | always |
serde | ^1 | derive | no | always |
serde_json | ^1 | — | no | always |
thiserror | ^2 | — | no | always |
tokio | ^1 | sync, time | no | always |
tracing | ^0.1 | — | no | always |
urlencoding | ^2 | — | no | always |
Development, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
tokio | ^1 | macros, rt-multi-thread, time | no | always |
wiremock | ^0.6 | — | no | always |
Build. None.
Depended on by. Nothing in this workspace.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR SELF["infrastructure-adapters-shodan"] SELF -->|runtime| n_infrastructure_fetcher["infrastructure-fetcher"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
| Feature | Enables | On by default |
|---|---|---|
default | secure-egress | yes |
secure-egress | dep:infrastructure-fetcher | yes |
flowchart LR n_default["default"] --> n_secure_egress["secure-egress"] n_secure_egress["secure-egress"] --> n_dep_infrastructure_fetcher["dep:infrastructure-fetcher"]
Targets
| Kind | Name | Source |
|---|---|---|
| lib | infrastructure_adapters_shodan | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | yes |
| database access | none detected |
| network I/O | yes |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
No workspace crate depends on this one.
Verification
| Kind | Count |
|---|---|
| Unit tests | 26 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 1 |
What the tests establish, by name:
debug_does_not_leak_api_key—src/auth.rsdefault_config_is_invalid_without_a_key—src/auth.rsempty_key_is_invalid_even_if_enabled—src/auth.rsnew_with_a_key_is_valid_and_enabled—src/auth.rsapi_info_parses_successful_response—src/client.rscreating_a_client_with_a_valid_config_succeeds—src/client.rscreating_a_client_with_an_invalid_config_fails—src/client.rsdns_domain_parses_successful_response—src/client.rsdns_resolve_parses_successful_response—src/client.rsdns_reverse_parses_successful_response—src/client.rsfrom_api_key_succeeds—src/client.rshost_parses_successful_response—src/client.rsmy_ip_strips_surrounding_quotes—src/client.rsnon_success_status_becomes_typed_api_error—src/client.rsnon_success_status_without_json_body_falls_back_to_http_status—src/client.rsrate_limit_enforces_minimum_interval_between_requests—src/client.rsstrict_egress_client_is_constructible—src/client.rsurl_embeds_path_and_key—src/client.rsurl_with_params_embeds_key_and_extra_params—src/client.rsshodan_api_error_deserializes—src/types.rsshodan_api_info_deserializes—src/types.rsshodan_dns_domain_deserializes—src/types.rsshodan_host_deserializes_minimal_response—src/types.rsshodan_host_with_service_and_vuln_deserializes—src/types.rsshodan_service_defaults_transport_to_tcp_when_absent—src/types.rsshodan_service_deserializes_with_default_transport—src/types.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 0 | 0 |
Public modules with a //! block | 0 | 0 |
Metrics
| Metric | Value |
|---|---|
| Rust source files | 5 |
| Source lines | 1002 |
| Code lines | 679 |
| Public API items | 0 |
| Public modules | 0 |
| Tests | 26 |
| Examples | 0 |
| Cargo features | 2 |
| Direct runtime dependencies | 8 |
| Workspace reverse dependencies | 0 |
pie showData
title Rust source composition
"Code" : 679
"Blank or comment" : 323
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.