Vendor-neutral IP-camera discovery: composes net-discovery sweep, adapter-based probing (Axis/ONVIF/RTSP), stable enrollment identity with scoped-MAC evidence. Assessment, not exploitation.
| Tier | operations |
| Role | unclassified (baselined) |
| Path | crates/operations/camera-discovery |
| Edition | 2021 |
| Targets | live_discover, live_probe, operations_camera_discovery |
| Public items | 85 across 12 modules |
| Tests | 55 |
What it is for
operations-camera-discovery — vendor-neutral IP-camera discovery.
Composes operations-net-discovery for the authorized CIDR sweep, resolves neighbor MACs, and probes camera-ish hosts through vendor adapters (Axis VAPIX now; ONVIF / generic RTSP later). Camera identity is a stable enrollment id with scoped-MAC evidence — a MAC alone is never the primary key (routed/NAT networks hide it; VLANs duplicate it). See docs/userstories/sprint-2.8-camera-discovery-harvest.md.
Posture: assessment, not exploitation — bounded, authorized probing only, mirroring operations-net-discovery.
Capabilities
axis (other)
Axis VAPIX adapter.
| Item |
|---|
fn axis_oui_confidence(mac : & Mac) -> MatchConfidence |
fn vapix_brand_url(ip : IpAddr) -> String |
fn mjpeg_url(ip : IpAddr, width : u16, height : u16) -> String |
fn parse_axis_model(body : & str) -> Option <String> |
AxisVapixAdapter
Axis VAPIX adapter.
| Item |
|---|
pub struct AxisVapixAdapter<H : CameraHttp> |
AxisVapixAdapter<H>
Axis VAPIX adapter.
| Item |
|---|
AxisVapixAdapter<H> :: fn new(http : H, creds : Option <Credentials>) -> Self |
AxisVapixAdapter<H> :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> |
discover (other)
Discovery orchestration with per-candidate failure isolation: a probe
| Item |
|---|
fn discover(candidates : & Candidate, probe : & dyn CandidateProbe) -> Vec <DiscoveryFinding> |
Candidate
Discovery orchestration with per-candidate failure isolation: a probe
| Item |
|---|
pub struct Candidate |
CandidateProbe
Discovery orchestration with per-candidate failure isolation: a probe
| Item |
|---|
pub trait CandidateProbe |
CompositeProbe
Discovery orchestration with per-candidate failure isolation: a probe
| Item |
|---|
pub struct CompositeProbe |
CompositeProbe :: fn new(probers : Vec <Box <dyn CandidateProbe>>) -> Self |
CompositeProbe :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> |
ProbeErr
Discovery orchestration with per-candidate failure isolation: a probe
| Item |
|---|
pub struct ProbeErr |
CameraDescriptor
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub struct CameraDescriptor |
DiscoveredCamera
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub struct DiscoveredCamera |
DiscoveredCamera :: fn new(id : CameraId, hardware : HardwareIdentity, ip : IpAddr, descriptor : CameraDescriptor, streams : Vec <StreamEndpoint>,) -> Self |
DiscoveryFinding
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub enum DiscoveryFinding |
StreamEndpoint
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub struct StreamEndpoint |
UnidentifiedReason
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub enum UnidentifiedReason |
Vendor
Discovery outcomes. Every responsive host yields an explicit
| Item |
|---|
pub enum Vendor |
identity (other)
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
fn refind(identity : & HardwareIdentity, fresh : & MacObservation) -> RebindResult |
CameraId
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct CameraId |
CameraId :: fn new_random() -> Self |
CameraId :: fn from_uuid(u : Uuid) -> Self |
CameraId :: fn as_uuid(& self) -> Uuid |
HardwareIdentity
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct HardwareIdentity |
HardwareIdentity :: fn new(macs : BTreeSet <ScopedMac>, serial : Option <Serial>, endpoint_uuid : Option <Uuid>,) -> Result <Self, IdentityError> |
HardwareIdentity :: fn macs(& self) -> & BTreeSet <ScopedMac> |
HardwareIdentity :: fn serial(& self) -> Option <& Serial> |
HardwareIdentity :: fn endpoint_uuid(& self) -> Option <Uuid> |
IdentityError
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub enum IdentityError |
MacObservation
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct MacObservation |
NetworkScopeId
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct NetworkScopeId |
NetworkScopeId :: fn new(s : impl Into <String>) -> Self |
NetworkScopeId :: fn as_str(& self) -> & str |
RebindResult
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub enum RebindResult |
ScopedMac
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct ScopedMac |
ScopedMac :: fn new(scope : NetworkScopeId, mac : Mac) -> Self |
Serial
Camera identity — a stable enrollment id backed by scoped hardware evidence.
| Item |
|---|
pub struct Serial |
Serial :: fn new(s : impl Into <String>) -> Self |
Serial :: fn as_str(& self) -> & str |
Mac
Mac — a validated MAC address, normalized to upper-colon form.
| Item |
|---|
pub struct Mac |
Mac :: fn parse(s : & str) -> Result <Self, MacError> |
Mac :: fn octets(& self) -> u8; 6 |
Mac :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result |
MacError
Mac — a validated MAC address, normalized to upper-colon form.
| Item |
|---|
pub enum MacError |
neighbor (other)
Neighbor (ARP/NDP) resolution — IP → scoped MAC by parsing the kernel
| Item |
|---|
fn parse_ip_neigh(output : & str, scope : & NetworkScopeId) -> Vec <(IpAddr, ScopedMac)> |
NeighborResolver
Neighbor (ARP/NDP) resolution — IP → scoped MAC by parsing the kernel
| Item |
|---|
pub trait NeighborResolver |
SystemNeighborResolver
Neighbor (ARP/NDP) resolution — IP → scoped MAC by parsing the kernel
| Item |
|---|
pub struct SystemNeighborResolver |
SystemNeighborResolver :: fn resolve(& self, scope : & NetworkScopeId) -> Vec <(IpAddr, ScopedMac)> |
onvif (other)
ONVIF / generic-RTSP adapter — the vendor-neutral counterpart to the Axis
| Item |
|---|
pub const RTSP_PORT: u16 |
pub const DEFAULT_RTSP_PORTS: & u16 |
pub const DEFAULT_STREAM_PATHS: & & str |
fn rtsp_stream_url(ip : IpAddr, port : u16, path : & str) -> String |
fn model_from_banner(server : Option <& str>) -> Option <String> |
RtspOnvifAdapter
ONVIF / generic-RTSP adapter — the vendor-neutral counterpart to the Axis
| Item |
|---|
pub struct RtspOnvifAdapter<R : RtspProbe> |
RtspOnvifAdapter<R>
ONVIF / generic-RTSP adapter — the vendor-neutral counterpart to the Axis
| Item |
|---|
RtspOnvifAdapter<R> :: fn new(rtsp : R) -> Self |
RtspOnvifAdapter<R> :: fn with_ports(mut self, ports : Vec <u16>) -> Self |
RtspOnvifAdapter<R> :: fn with_stream_paths(mut self, paths : Vec <String>) -> Self |
RtspOnvifAdapter<R> :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> |
RtspOnvifAdapter<TcpRtsp>
ONVIF / generic-RTSP adapter — the vendor-neutral counterpart to the Axis
| Item |
|---|
RtspOnvifAdapter<TcpRtsp> :: fn tcp() -> Self |
MatchConfidence
The probe seam — how strongly an adapter claims a candidate.
| Item |
|---|
pub enum MatchConfidence |
rtsp (other)
RTSP transport seam for camera fingerprinting.
| Item |
|---|
fn build_options_request(ip : IpAddr, port : u16) -> String |
fn parse_options_response(raw : & str) -> Result <RtspInfo, RtspError> |
RtspError
RTSP transport seam for camera fingerprinting.
| Item |
|---|
pub enum RtspError |
RtspInfo
RTSP transport seam for camera fingerprinting.
| Item |
|---|
pub struct RtspInfo |
RtspInfo :: fn is_rtsp_server(& self) -> bool |
RtspLimits
RTSP transport seam for camera fingerprinting.
| Item |
|---|
pub struct RtspLimits |
RtspLimits :: fn default() -> Self |
RtspProbe
RTSP transport seam for camera fingerprinting.
| Item |
|---|
pub trait RtspProbe |
TcpRtsp
RTSP transport seam for camera fingerprinting.
| Item |
|---|
pub struct TcpRtsp |
TcpRtsp :: fn options(& self, ip : IpAddr, port : u16) -> Result <RtspInfo, RtspError> |
HostPorts
Discovery orchestration source — composes an authorized sweep + neighbor
| Item |
|---|
pub struct HostPorts |
Sweeper
Discovery orchestration source — composes an authorized sweep + neighbor
| Item |
|---|
pub trait Sweeper |
fn discover_source(scope : & NetworkScopeId, sweeper : & dyn Sweeper, resolver : & dyn NeighborResolver, probe : & dyn CandidateProbe,) -> Vec <DiscoveryFinding> |
SsrfError
SSRF guard for probe (and redirect) targets.
| Item |
|---|
pub enum SsrfError |
fn check_probe_target(target : IpAddr, authorized : & IpAddr) -> Result <(), SsrfError> |
CameraHttp
The HTTP transport seam for probing cameras.
| Item |
|---|
pub trait CameraHttp |
Credentials
The HTTP transport seam for probing cameras.
| Item |
|---|
pub struct Credentials |
CurlHttp
The HTTP transport seam for probing cameras.
| Item |
|---|
pub struct CurlHttp |
CurlHttp :: fn fetch(& self, url : & str, creds : Option <& Credentials>, limits : & FetchLimits,) -> Result <Vec <u8>, HttpError> |
FetchLimits
The HTTP transport seam for probing cameras.
| Item |
|---|
pub struct FetchLimits |
FetchLimits :: fn default() -> Self |
HttpError
The HTTP transport seam for probing cameras.
| Item |
|---|
pub enum HttpError |
How to use it
From `examples/live_discover.rs`:
#[cfg(feature = "curl")]
fn main() {
use operations_camera_discovery::{
AxisVapixAdapter, Candidate, CandidateProbe, Credentials, CurlHttp, Mac, NetworkScopeId,
ScopedMac,
};
let a: Vec<String> = std::env::args().collect();
let ip: std::net::IpAddr = a
.get(1)
.expect("usage: <ip> <mac> [user] [pass]")
.parse()
.expect("valid ip");
let mac = Mac::parse(a.get(2).expect("need mac")).expect("valid mac");
let creds = match (a.get(3), a.get(4)) {
(Some(u), Some(p)) => Some(Credentials {
user: u.clone(),
From `examples/live_probe.rs`:
#[cfg(feature = "curl")]
fn main() {
use operations_camera_discovery::{CameraHttp, Credentials, CurlHttp, FetchLimits};
let args: Vec<String> = std::env::args().collect();
let url = match args.get(1) {
Some(u) => u,
None => {
eprintln!("usage: live_probe <url> [user] [pass]");
std::process::exit(2);
}
};
let creds = match (args.get(2), args.get(3)) {
(Some(u), Some(p)) => Some(Credentials {
user: u.clone(),
pass: p.clone(),
}),
Module structure
operations_camera_discovery
axisdiscoverfindingsidentitymacneighboronvifprobertspsourcessrftransport
flowchart TD n_operations_camera_discovery["operations_camera_discovery"] n_operations_camera_discovery --> n_axis["axis"] n_operations_camera_discovery --> n_discover["discover"] n_operations_camera_discovery --> n_findings["findings"] n_operations_camera_discovery --> n_identity["identity"] n_operations_camera_discovery --> n_mac["mac"] n_operations_camera_discovery --> n_neighbor["neighbor"] n_operations_camera_discovery --> n_onvif["onvif"] n_operations_camera_discovery --> n_probe["probe"] n_operations_camera_discovery --> n_rtsp["rtsp"] n_operations_camera_discovery --> n_source["source"] n_operations_camera_discovery --> n_ssrf["ssrf"] n_operations_camera_discovery --> n_transport["transport"]
Public surface
`axis`
| Item | What it is |
|---|---|
fn axis_oui_confidence(mac : & Mac) -> MatchConfidence | OUI-based confidence that a MAC is an Axis device |
fn vapix_brand_url(ip : IpAddr) -> String | Brand/model probe URL (the response carries Brand.ProdNbr/ProdShortName) |
fn mjpeg_url(ip : IpAddr, width : u16, height : u16) -> String | The MJPEG live-view URL for a camera at a given resolution. |
fn parse_axis_model(body : & str) -> Option <String> | Parse the Axis model (Brand.ProdNbr) from a VAPIX group=Brand response. |
pub struct AxisVapixAdapter<H : CameraHttp> | The Axis vendor adapter — probes a candidate over VAPIX and, on success, yields a DiscoveredCamera |
AxisVapixAdapter<H> :: fn new(http : H, creds : Option <Credentials>) -> Self | — |
AxisVapixAdapter<H> :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> | — |
`discover`
| Item | What it is |
|---|---|
pub struct Candidate | A responsive host to be probed, from the sweep + neighbor-resolution stage. |
pub struct ProbeErr | A per-candidate probe failure — mapped to a ProbeFailed finding. |
pub trait CandidateProbe | The probe seam (mockable) |
pub struct CompositeProbe | Try several probers in order and take the first that identifies a camera |
CompositeProbe :: fn new(probers : Vec <Box <dyn CandidateProbe>>) -> Self | Build from probers in priority order (most specific first). |
CompositeProbe :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> | — |
fn discover(candidates : & Candidate, probe : & dyn CandidateProbe) -> Vec <DiscoveryFinding> | Probe every candidate, isolating per-candidate failures |
`findings`
| Item | What it is |
|---|---|
pub enum Vendor | The vendor family a probe identified. |
pub struct CameraDescriptor | Descriptive (non-identity) attributes |
pub struct StreamEndpoint | A stream endpoint a camera exposes (URL comes from an authenticated probe, never blindly trusted from a camera-supplied field). |
pub struct DiscoveredCamera | A fully identified + probed camera |
DiscoveredCamera :: fn new(id : CameraId, hardware : HardwareIdentity, ip : IpAddr, descriptor : CameraDescriptor, streams : Vec <StreamEndpoint>,) -> Self | — |
pub enum UnidentifiedReason | Why a responsive host could not become a camera identity. |
pub enum DiscoveryFinding | The outcome for one candidate host. |
`identity`
| Item | What it is |
|---|---|
pub struct NetworkScopeId | An operator-scoped L2 domain a MAC observation belongs to |
NetworkScopeId :: fn new(s : impl Into <String>) -> Self | — |
NetworkScopeId :: fn as_str(& self) -> & str | — |
pub struct ScopedMac | A MAC qualified by the network scope it was observed in. |
ScopedMac :: fn new(scope : NetworkScopeId, mac : Mac) -> Self | — |
pub struct Serial | A camera-reported serial number (identity evidence that survives IP/MAC change). |
Serial :: fn new(s : impl Into <String>) -> Self | — |
Serial :: fn as_str(& self) -> & str | — |
pub struct CameraId | The stable identity assigned to a camera at enrollment. |
CameraId :: fn new_random() -> Self | Assign a fresh random identity (enrollment). |
CameraId :: fn from_uuid(u : Uuid) -> Self | — |
CameraId :: fn as_uuid(& self) -> Uuid | — |
pub enum IdentityError | Why a HardwareIdentity could not be constructed. |
pub struct HardwareIdentity | The evidence bundle that lets a camera be re-matched across IP/MAC churn |
HardwareIdentity :: fn new(macs : BTreeSet <ScopedMac>, serial : Option <Serial>, endpoint_uuid : Option <Uuid>,) -> Result <Self, IdentityError> | Construct from evidence |
HardwareIdentity :: fn macs(& self) -> & BTreeSet <ScopedMac> | — |
HardwareIdentity :: fn serial(& self) -> Option <& Serial> | — |
HardwareIdentity :: fn endpoint_uuid(& self) -> Option <Uuid> | — |
pub struct MacObservation | A fresh (scoped MAC -> current IP) observation from a sweep. |
pub enum RebindResult | Result of re-binding a known identity to its current IP after a fresh sweep. |
fn refind(identity : & HardwareIdentity, fresh : & MacObservation) -> RebindResult | Re-bind a known HardwareIdentity to its current IP using fresh sweep observations, matching on scoped MAC |
`mac`
| Item | What it is |
|---|---|
pub enum MacError | Why a MAC string could not become a Mac. |
pub struct Mac | A validated, normalized unicast MAC address. |
Mac :: fn parse(s : & str) -> Result <Self, MacError> | Parse a MAC from aa:bb:cc:dd:ee:ff / aa-bb-... (any case) |
Mac :: fn octets(& self) -> u8; 6 | The raw six octets. |
Mac :: fn fmt(& self, f : & mut fmt::Formatter <'_>) -> fmt::Result | — |
`neighbor`
| Item | What it is |
|---|---|
fn parse_ip_neigh(output : & str, scope : & NetworkScopeId) -> Vec <(IpAddr, ScopedMac)> | Parse ip neigh show output into (IP, scoped MAC) pairs within scope |
pub trait NeighborResolver | Resolve IP → scoped MAC for a network scope (the ARP/NDP layer). |
pub struct SystemNeighborResolver | Live resolver: shells ip neigh show as a strict arg-vector (never a shell) and parses it with parse_ip_neigh |
SystemNeighborResolver :: fn resolve(& self, scope : & NetworkScopeId) -> Vec <(IpAddr, ScopedMac)> | — |
`onvif`
| Item | What it is |
|---|---|
pub const RTSP_PORT: u16 | The default RTSP port. |
pub const DEFAULT_RTSP_PORTS: & u16 | The RTSP-family ports the adapter engages on by default, in probe priority order: the standard port first, then the common alternates (8554 alt-RTSP, 8000 used by some ODM firmwares) |
pub const DEFAULT_STREAM_PATHS: & & str | Default candidate stream paths (main first), covering the common ODM firmwares |
fn rtsp_stream_url(ip : IpAddr, port : u16, path : & str) -> String | Build an RTSP stream URL |
fn model_from_banner(server : Option <& str>) -> Option <String> | Derive a short model hint from an RTSP Server: banner |
pub struct RtspOnvifAdapter<R : RtspProbe> | ONVIF / generic-RTSP adapter over the RtspProbe seam. |
RtspOnvifAdapter<R> :: fn new(rtsp : R) -> Self | Build with the default RTSP port set + candidate path list. |
RtspOnvifAdapter<R> :: fn with_ports(mut self, ports : Vec <u16>) -> Self | Override the RTSP ports the adapter engages on (probe priority order). |
RtspOnvifAdapter<R> :: fn with_stream_paths(mut self, paths : Vec <String>) -> Self | Override the candidate stream paths (a new ODM firmware = a config add). |
RtspOnvifAdapter<TcpRtsp> :: fn tcp() -> Self | The production adapter over std::net::TcpStream. |
RtspOnvifAdapter<R> :: fn probe_one(& self, c : & Candidate) -> Result <DiscoveryFinding, ProbeErr> | — |
`probe`
| Item | What it is |
|---|---|
pub enum MatchConfidence | How strongly a camera-probe adapter believes it can handle a candidate. |
`rtsp`
| Item | What it is |
|---|---|
pub struct RtspInfo | What an RTSP OPTIONS exchange tells us about a host. |
RtspInfo :: fn is_rtsp_server(& self) -> bool | Does this look like a real RTSP server? A 200 OK that advertises the core playback methods — not merely something listening on 554. |
pub enum RtspError | An RTSP probe failure — mapped by the adapter to a ProbeErr. |
pub struct RtspLimits | Bounds on an RTSP probe (mirrors FetchLimits): connect/read timeouts and a hard response cap against a slow-loris / oversized banner. |
RtspLimits :: fn default() -> Self | — |
pub trait RtspProbe | The RTSP probe seam (mockable) |
fn build_options_request(ip : IpAddr, port : u16) -> String | Build the raw OPTIONS request bytes |
fn parse_options_response(raw : & str) -> Result <RtspInfo, RtspError> | Parse an RTSP OPTIONS response into RtspInfo |
pub struct TcpRtsp | Production RTSP probe over std::net::TcpStream |
TcpRtsp :: fn options(& self, ip : IpAddr, port : u16) -> Result <RtspInfo, RtspError> | — |
`source`
| Item | What it is |
|---|---|
pub struct HostPorts | A responsive host from an authorized sweep. |
pub trait Sweeper | The authorized CIDR sweep seam |
fn discover_source(scope : & NetworkScopeId, sweeper : & dyn Sweeper, resolver : & dyn NeighborResolver, probe : & dyn CandidateProbe,) -> Vec <DiscoveryFinding> | Full discovery: sweep → resolve neighbor MACs → build candidates → probe, isolating per-candidate failures |
`ssrf`
| Item | What it is |
|---|---|
pub enum SsrfError | Why a probe target was rejected. |
fn check_probe_target(target : IpAddr, authorized : & IpAddr) -> Result <(), SsrfError> | Reject a probe/redirect target unless it is an authorized, routable unicast host |
`transport`
| Item | What it is |
|---|---|
pub struct FetchLimits | Bounds on every camera fetch (Gate 1.5 security review): timeouts plus a hard body cap against slow-loris and XML-bomb responses. |
FetchLimits :: fn default() -> Self | — |
pub struct Credentials | Camera credentials — carried out-of-band, never placed in a URL or argv. |
pub enum HttpError | A camera-probe transport failure. |
pub trait CameraHttp | Fetch a camera URL (digest/anyauth applied by the impl) |
pub struct CurlHttp | In-process libcurl transport (Gate 1.5 tiebreak), behind the curl feature |
CurlHttp :: fn fetch(& self, url : & str, creds : Option <& Credentials>, limits : & FetchLimits,) -> Result <Vec <u8>, HttpError> | — |
Re-exports. Exported here, defined elsewhere.
| Export | Defined in |
|---|---|
AxisVapixAdapter | axis::AxisVapixAdapter |
CurlHttp | transport::CurlHttp |
MatchConfidence | probe::MatchConfidence |
{CameraDescriptor,DiscoveredCamera,DiscoveryFinding,StreamEndpoint,UnidentifiedReason,Vendor,} | findings::{CameraDescriptor,DiscoveredCamera,DiscoveryFinding,StreamEndpoint,UnidentifiedReason,Vendor,} |
{CameraHttp,Credentials,FetchLimits,HttpError} | transport::{CameraHttp,Credentials,FetchLimits,HttpError} |
{Mac,MacError} | mac::{Mac,MacError} |
{RtspError,RtspInfo,RtspLimits,RtspProbe,TcpRtsp} | rtsp::{RtspError,RtspInfo,RtspLimits,RtspProbe,TcpRtsp} |
{RtspOnvifAdapter,DEFAULT_RTSP_PORTS,DEFAULT_STREAM_PATHS,RTSP_PORT} | onvif::{RtspOnvifAdapter,DEFAULT_RTSP_PORTS,DEFAULT_STREAM_PATHS,RTSP_PORT} |
{check_probe_target,SsrfError} | ssrf::{check_probe_target,SsrfError} |
{discover,Candidate,CandidateProbe,CompositeProbe,ProbeErr} | discover::{discover,Candidate,CandidateProbe,CompositeProbe,ProbeErr} |
{discover_source,HostPorts,Sweeper} | source::{discover_source,HostPorts,Sweeper} |
{parse_ip_neigh,NeighborResolver,SystemNeighborResolver} | neighbor::{parse_ip_neigh,NeighborResolver,SystemNeighborResolver} |
{refind,CameraId,HardwareIdentity,IdentityError,MacObservation,NetworkScopeId,RebindResult,ScopedMac,Serial,} | identity::{refind,CameraId,HardwareIdentity,IdentityError,MacObservation,NetworkScopeId,RebindResult,ScopedMac,Serial,} |
Boundary
Depends on no other workspace tier.
Shares tier operations with 40 other crates: operations-approval-workflow, operations-assessments, operations-block-imaging, operations-boot-media, operations-browser-agent-worker, operations-camera-liveview, operations-camera-registry, operations-compliance, … (40 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) | operations |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/operations/camera-discovery |
| Vocabulary in force (lexicon) | current |
Dependencies
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
curl | ^0.4 | — | yes | always |
thiserror | ^2 | — | no | always |
uuid | ^1 | v4, v7, serde, js | no | always |
Development. None.
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_domain_recordings["domain-recordings"] -->|uses| SELF n_operations_camera_registry["operations-camera-registry"] -->|uses| SELF SELF["operations-camera-discovery"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
| Feature | Enables | On by default |
|---|---|---|
curl | dep:curl, curl/static-curl | no |
flowchart LR n_curl["curl"] --> n_dep_curl["dep:curl"] n_curl["curl"] --> n_curl_static_curl["curl/static-curl"]
Targets
| Kind | Name | Source |
|---|---|---|
| example | live_discover | `examples/live_discover.rs` |
| example | live_probe | `examples/live_probe.rs` |
| lib | operations_camera_discovery | `src/lib.rs` |
Error model
| Error type | Named by |
|---|---|
HttpError | declared, no public signature returns it |
IdentityError | declared, no public signature returns it |
MacError | declared, no public signature returns it |
RtspError | parse_options_response |
SsrfError | check_probe_target |
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | none detected |
| database access | none detected |
| network I/O | none detected |
| 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
2 workspace crates depend on this one: domain-recordings, operations-camera-registry.
Verification
| Kind | Count |
|---|---|
| Unit tests | 55 |
| Integration tests | 0 |
| Examples | 2 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
axis | 5 | 1 | 0 |
discover | 5 | 4 | 0 |
findings | 6 | 0 | 4 |
identity | 9 | 2 | 3 |
mac | 2 | 1 | 0 |
neighbor | 3 | 0 | 0 |
onvif | 6 | 0 | 0 |
probe | 1 | 0 | 0 |
rtsp | 7 | 0 | 0 |
source | 3 | 0 | 0 |
ssrf | 2 | 0 | 0 |
transport | 5 | 4 | 1 |
What the tests establish, by name:
adapter_maps_transport_error_to_probe_err—src/axis.rsadapter_probes_axis_into_camera—src/axis.rsadapter_reports_unidentified_when_no_mac—src/axis.rsaxis_oui_confidence_classifies—src/axis.rscredentials_never_in_probe_url—src/axis.rsipv6_url_is_bracketed—src/axis.rsmjpeg_url_shape—src/axis.rsparse_axis_model_extracts_prodnbr—src/axis.rsvapix_brand_url_shape—src/axis.rscomposite_isolates_a_prober_error_and_still_finds_a_later_camera—src/discover.rscomposite_keeps_the_most_informative_when_no_camera—src/discover.rscomposite_returns_the_first_camera_and_stops—src/discover.rsone_failure_does_not_abort_sweep—src/discover.rsidentity_accepts_mac_only—src/identity.rsidentity_accepts_serial_only—src/identity.rsidentity_rejects_empty_evidence—src/identity.rsrefind_ambiguous_on_two_distinct_ips—src/identity.rsrefind_binds_single_match—src/identity.rsrefind_dedups_identical_observations_to_bound—src/identity.rsrefind_notfound_when_no_match—src/identity.rsrefind_scope_mismatch_is_notfound—src/identity.rsparse_accepts_valid_unicast—src/mac.rsparse_normalizes_dash_and_case—src/mac.rsparse_rejects_broadcast—src/mac.rsparse_rejects_malformed—src/mac.rsparse_rejects_multicast—src/mac.rsparse_rejects_nil—src/mac.rsdrops_unusable_lladdr—src/neighbor.rsparses_ipv6_neighbor—src/neighbor.rsparses_reachable_and_stale_neighbors—src/neighbor.rs- _… 25 more_
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 64 | 85 |
Public modules with a //! block | 12 | 12 |
pie showData
title Public items with rustdoc
"Documented" : 64
"No rustdoc detected" : 21
Metrics
| Metric | Value |
|---|---|
| Rust source files | 13 |
| Source lines | 2291 |
| Code lines | 1755 |
| Public API items | 85 |
| Public modules | 12 |
| Tests | 55 |
| Examples | 2 |
| Cargo features | 1 |
| Direct runtime dependencies | 3 |
| Workspace reverse dependencies | 2 |
pie showData
title Public API by kind
"constant" : 3
"enum" : 10
"function" : 13
"method" : 31
"struct" : 23
"trait" : 5
pie showData
title Rust source composition
"Code" : 1755
"Blank or comment" : 536
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.