Skip to main content

Crate host_identity

Crate host_identity 

Source
Expand description

Stable, collision-resistant host identity.

Many agents and telemetry pipelines need a stable identifier for the host they run on: one that survives restarts and upgrades but distinguishes two otherwise-identical hosts. The obvious source on modern Linux is /etc/machine-id, but relying on it alone is unreliable: cloned VMs share IDs, LXC guests often inherit the host’s ID, minimal container images have no file at all, and systemd writes the literal string uninitialized during early boot.

host-identity exposes every known-good identity source as a composable Source implementation. Consumers can either take the default platform-appropriate chain or mix and match sources in any order to match their own policy.

§The common case

// Local-only default chain: env override → platform sources →
// (container ID when applicable). No network calls.
let id = host_identity::resolve()?;
println!("{id}");

When you want cloud-metadata endpoints in the chain as well, use [resolve_with_transport] with your HTTP client of choice. That chain is strictly richer than the local default: it includes every cloud and Kubernetes source the feature set enabled, ordered so that per-pod identity outranks per-container outranks per-instance outranks per-host software state.

§Identifier-based chains (config-driven)

For operator-facing config files, build a chain from a list of short string identifiers:

use host_identity::ids::{resolver_from_ids, source_ids};

// Equivalent to:
//   Resolver::new()
//       .push(EnvOverride::new("HOST_IDENTITY"))
//       .push(MachineIdFile::default())
//       .push(DmiProductUuid::default())
let resolver = resolver_from_ids([
    source_ids::ENV_OVERRIDE,
    source_ids::MACHINE_ID,
    source_ids::DMI,
]).unwrap();

See ids for the full list of identifiers and the _with_transport variant for cloud sources.

§Auditing every source

resolve_all and [resolve_all_with_transport] walk the same chains without short-circuiting and return one ResolveOutcome per source. Use them when you want to see what every source would produce — operator diagnostics, debugging, cross-validation. To audit a caller-chosen subset, build the resolver with exactly those sources and call Resolver::resolve_all:

use host_identity::Resolver;
use host_identity::sources::{MachineIdFile, DmiProductUuid};

let outcomes = Resolver::new()
    .push(MachineIdFile::default())
    .push(DmiProductUuid::default())
    .resolve_all();
for outcome in outcomes {
    println!("{:?} → {:?}", outcome.source(), outcome.host_id());
}

§Mixing and matching

Every built-in source is a public type that implements Source. Chain them in any order, add your own, and pick the wrap strategy:

use host_identity::{Resolver, Wrap};
use host_identity::sources::{EnvOverride, FileOverride, MachineIdFile, DmiProductUuid};

let id = Resolver::new()
    .push(EnvOverride::new("MY_APP_HOST_ID"))
    .push(DmiProductUuid::default())    // SMBIOS first — stable across OS reinstalls
    .push(MachineIdFile::default())
    .push(FileOverride::new("/etc/my-app/host-id"))
    .with_wrap(Wrap::UuidV5Namespaced)
    .resolve()?;

§Starting from the defaults

Resolver::with_defaults pre-loads the platform’s default chain. Use Resolver::prepend to add higher-priority sources (e.g. your own override) or Resolver::push to add fallbacks:

use host_identity::Resolver;
use host_identity::sources::FileOverride;

let id = Resolver::with_defaults()
    .push(FileOverride::new("/etc/host-identity"))  // last-resort fallback
    .resolve()?;

§Custom sources

Implement Source directly, or wrap a closure with sources::FnSource:

use host_identity::sources::FnSource;
use host_identity::{Resolver, SourceKind};

let custom = FnSource::new(SourceKind::custom("hsm"), || {
    // Read from an HSM, a custom config file, an in-house identity
    // service, etc.
    Ok(Some("h-0abc1234".to_owned()))
});

let id = Resolver::new().push(custom).resolve()?;

§Cloud-metadata sources

Each major cloud provider has a dedicated source behind an opt-in feature flag. Sources are generic over a caller-supplied [transport::HttpTransport]; the crate ships no HTTP client of its own.

SourceFeature
[sources::AwsImds]aws
[sources::GcpMetadata]gcp
[sources::AzureImds]azure
[sources::DigitalOceanMetadata]digitalocean
[sources::HetznerMetadata]hetzner
[sources::OciMetadata]oci
[sources::OpenStackMetadata]openstack

Implement [transport::HttpTransport] for your HTTP client of choice (adapters are ~10 lines against reqwest, ureq, hyper, etc.), or pass a closure — a blanket impl accepts any Fn(http::Request<Vec<u8>>) -> Result<http::Response<Vec<u8>>, E>. For providers the crate doesn’t ship, implement [sources::CloudEndpoint] on a zero-sized type and alias [sources::CloudMetadata].

§Kubernetes sources

Feature k8s (no new dependencies) exposes [sources::KubernetesPodUid] (from /proc/self/mountinfo), [sources::KubernetesServiceAccount] (from the mounted SA secret), and [sources::KubernetesDownwardApi] (any file projected by a downwardAPI volume).

Re-exports§

pub use ids::UnknownSourceError;
pub use ids::resolver_from_ids;

Modules§

ids
Identifier-based chain construction.
sources
Built-in Source implementations.

Structs§

HostId
A stable host identifier.
HostIdSummary
Display wrapper returned by HostId::summary.
Probe
A raw identifier returned by a Source, before UUID wrapping.
Resolver
A composable chain of identity sources.

Enums§

Error
Errors returned by crate::Resolver::resolve.
ResolveOutcome
One source’s outcome in a full-chain walk.
SourceKind
Short, stable label identifying a source.
Wrap
How the raw identifier produced by a crate::Source is turned into a uuid::Uuid.

Constants§

DEFAULT_NAMESPACE
Namespace used for the default UUID v5 wrap strategy.

Traits§

Source
A single identity source.

Functions§

resolve
Resolve a stable host identity using the default chain for this platform.
resolve_all
Walk the default local chain without short-circuiting and return every source’s ResolveOutcome in order.