How to Use Memory Address Lookup in iroh

Use the MemoryLookup struct from iroh/src/address_lookup/memory.rs to manually store and resolve EndpointId to EndpointAddr mappings at runtime, attaching it to your Endpoint builder via the address_lookup method.

When automatic discovery via DNS or pkarr is unavailable, iroh provides a memory address lookup mechanism for out-of-band address resolution. The MemoryLookup implementation allows you to programmatically register relay URLs and direct transport addresses, making it ideal for applications that receive addressing data through private channels like EndpointTicket exchanges.

What Is Memory Address Lookup in iroh?

The AddressLookup trait in iroh/src/address_lookup.rs defines how iroh resolves an EndpointId into a network-specific EndpointAddr. MemoryLookup provides a concrete, in-memory implementation of this trait, storing address data in a thread-safe BTreeMap wrapped in Arc<RwLock<...>>. This design allows multiple threads to query and update address information concurrently without blocking the main endpoint loop.

Each lookup entry carries a provenance string—defaulting to "memory_lookup"—that appears in resolution results, which helps distinguish manually injected addresses from those discovered via other mechanisms.

Creating and Configuring MemoryLookup

You can instantiate MemoryLookup using several constructors depending on your initialization requirements.

Basic Initialization

Create an empty registry or customize the provenance metadata for debugging purposes:

use iroh::address_lookup::memory::MemoryLookup;

// Empty lookup with default provenance
let lookup = MemoryLookup::new();

// Or specify a custom provenance string
let lookup = MemoryLookup::with_provenance("my_app_config");

Bulk Loading from EndpointInfo

When bootstrapping from a stored list of known peers, use MemoryLookup::from_endpoint_info() to populate the registry in a single call:

use iroh::address_lookup::memory::MemoryLookup;
use iroh_base::{EndpointInfo, SecretKey};

let infos = vec![
    EndpointInfo::new(SecretKey::generate().public())
        .with_relay("https://relay1.example.com".parse().unwrap()),
    EndpointInfo::new(SecretKey::generate().public())
        .with_relay("https://relay2.example.com".parse().unwrap()),
];

let memory_lookup = MemoryLookup::from_endpoint_info(infos);

Managing Endpoint Data at Runtime

MemoryLookup provides distinct methods for updating stored addressing information depending on whether you want to merge or replace existing data.

Adding New Endpoint Information

The add_endpoint_info() method merges new address data with any existing entry for the same EndpointId, accumulating multiple transport addresses:

use iroh_base::{EndpointAddr, SecretKey, TransportAddr};

let id = SecretKey::generate().public();
let addr = EndpointAddr {
    id,
    addrs: [TransportAddr::Relay("https://relay.example.com".parse().unwrap())]
        .into_iter()
        .collect(),
};

memory_lookup.add_endpoint_info(addr);

Replacing Existing Records

Use set_endpoint_info() to completely overwrite an entry, which is critical when handling credential rotations or updated relay URLs:

// Overwrites any previous entry for this EndpointId
memory_lookup.set_endpoint_info(new_endpoint_info);

Thread Safety Considerations

Because MemoryLookup uses an internal Arc<RwLock<BTreeMap<EndpointId, StoredEndpointInfo>>>, you can safely clone the lookup handle and share it between your endpoint initialization code and the runtime components that receive address updates via network messages or file watchers.

Attaching Memory Lookup to Your Endpoint

Register your MemoryLookup instance with the Endpoint builder before binding. When Endpoint::connect(id) is called, iroh queries the lookup; if the EndpointId exists, the lookup yields a Stream of Item structs containing the stored EndpointData and provenance.

use iroh::{Endpoint, endpoint::presets};

// Clone the lookup so you can update it later
let address_lookup = MemoryLookup::new();

let ep = Endpoint::builder(presets::N0)
    .address_lookup(address_lookup.clone())
    .bind()
    .await?;

// Later, inject out-of-band address data
let id = SecretKey::generate().public();
address_lookup.add_endpoint_info(
    iroh_base::EndpointAddr {
        id,
        addrs: [TransportAddr::Relay("https://relay.example.com".parse()?)]
            .into_iter()
            .collect(),
    },
);

// Connect using only the EndpointId—the lookup supplies the address
let conn = ep.connect(id).await?;

Summary

  • MemoryLookup in iroh/src/address_lookup/memory.rs implements the AddressLookup trait for manual address resolution.
  • Thread-safe updates are enabled via Arc<RwLock<BTreeMap<...>>> internals, allowing concurrent access across your application.
  • Flexible construction via new(), with_provenance(), or from_endpoint_info() supports both incremental and bulk initialization.
  • Additive or replacement updates use add_endpoint_info() (merge) or set_endpoint_info() (overwrite) depending on your consistency requirements.
  • Integration requires passing the lookup to Endpoint::builder(presets::N0).address_lookup(...) before binding.

Frequently Asked Questions

How does memory address lookup differ from DNS-based discovery in iroh?

DNS and pkarr provide automatic public discovery, whereas MemoryLookup is a private, application-controlled registry. According to the iroh source code in iroh/src/address_lookup.rs, the AddressLookup trait abstracts over bothautomatic and manual sources; MemoryLookup specifically handles cases where you receive addressing data through out-of-band channels like EndpointTicket exchanges or local configuration files.

Is MemoryLookup thread-safe for concurrent updates?

Yes. The implementation in iroh/src/address_lookup/memory.rs wraps the internal BTreeMap in Arc<RwLock<...>>, allowing you to clone the MemoryLookup handle and share it across threads. One thread can update addresses via add_endpoint_info() while another calls Endpoint::connect(), with both operations executing safely without data races.

What happens if an EndpointId is not found in the MemoryLookup?

If the EndpointId is not present in the lookup’s internal map, the MemoryLookup returns an empty stream of Items. As implemented in the iroh connection logic, the endpoint will then fall back to other registered address lookup implementations or eventually fail to establish a connection if no addresses can be resolved.

Can I use MemoryLookup alongside other address lookup implementations?

Yes. While the Endpoint builder accepts a single AddressLookup implementation, iroh supports composition through wrapper types that implement the trait. You can chain MemoryLookup with other lookups by creating a custom implementation that queries your in-memory registry first, then delegates to secondary sources if the initial lookup returns no results.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →