multiformats/py-multiaddr

multiaddr implementation in Python

36

stars

369

commits

Python

primary language

Sep 6, 2026

updated

multiformats.io/
ipfs
libp2p
multiformats
network
python

README

py-multiaddr
==========================

.. image:: https://img.shields.io/pypi/v/multiaddr.svg
        :target: https://pypi.python.org/pypi/multiaddr

.. image:: https://github.com/multiformats/py-multiaddr/actions/workflows/ci.yml/badge.svg
        :target: https://github.com/multiformats/py-multiaddr/actions

.. image:: https://codecov.io/github/multiformats/py-multiaddr/coverage.svg?branch=master
        :target: https://codecov.io/github/multiformats/py-multiaddr?branch=master

.. image:: https://readthedocs.org/projects/multiaddr/badge/?version=latest
        :target: https://readthedocs.org/projects/multiaddr/?badge=latest
        :alt: Documentation Status
..

    multiaddr_ implementation in Python

.. _multiaddr: https://github.com/multiformats/multiaddr

..


.. contents:: :local:

Installation
============

.. code-block:: bash

    pip install multiaddr

Requirements
------------

- Python 3.10+
- trio (for async DNS resolution)

Usage
=====

CLI Tool
--------

py-multiaddr provides a command line interface to inspect multiaddrs and decode them into structured JSON.

.. code-block:: bash

    $ multiaddr /ip4/1.2.3.4/tcp/80
    {
      "string": "/ip4/1.2.3.4/tcp/80",
      "packed": "0x0401020304060050",
      "packedSize": 8,
      "components": [
        {
          "protocol": "ip4",
          "code": 4,
          "value": "1.2.3.4",
          "rawValue": "0x01020304"
        },
        {
          "protocol": "tcp",
          "code": 6,
          "value": "80",
          "rawValue": "0x0050"
        }
      ]
    }

You can also pass a hex-encoded multiaddr prefixed with ``0x``, e.g.
``multiaddr 0x0401020304060050``. Use the ``-c`` or ``--compact`` flag to
output on a single line.

IP Classification
-----------------

You can easily classify the IP components of multiaddrs (e.g. check for loopback, private networks, or NAT64):

.. code-block:: python

    from multiaddr import Multiaddr, is_private_addr, is_ip_loopback

    ma1 = Multiaddr("/ip4/192.168.1.1/tcp/80")
    print(is_private_addr(ma1))  # True
    print(is_ip_loopback(ma1))   # False

    ma2 = Multiaddr("/ip4/127.0.0.1")
    print(is_private_addr(ma2))  # True
    print(is_ip_loopback(ma2))   # True

Other available helpers include ``is_public_addr``, ``is_ip_unspecified``,
``is_ip6_link_local``, ``is_thin_waist``, and ``is_nat64_ipv4_converted_ipv6_addr``.
These operate on ``Multiaddr`` objects; ``is_wildcard`` / ``is_link_local_ip``
remain available for raw IP strings.

Simple
------

.. code-block:: python

    from multiaddr import Multiaddr

    # construct from a string
    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

    # construct from bytes
    #m2 = Multiaddr(bytes_addr=m1.to_bytes()) # deprecated
    m2 = Multiaddr(m1.to_bytes())

    assert str(m1) == "/ip4/127.0.0.1/udp/1234"
    assert str(m1) == str(m2)
    assert m1.to_bytes() == m2.to_bytes()
    assert m1 == m2
    assert m2 == m1
    assert not (m1 != m2)
    assert not (m2 != m1)


Protocols
---------

.. code-block:: python

    from multiaddr import Multiaddr

    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

    # get the multiaddr protocol description objects
    m1.protocols()
    # [Protocol(code=4, name='ip4', size=32), Protocol(code=17, name='udp', size=16)]


En/decapsulate
--------------

.. code-block:: python

    from multiaddr import Multiaddr

    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
    m1.encapsulate(Multiaddr("/sctp/5678"))
    # <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
    m1.decapsulate(Multiaddr("/udp"))
    # <Multiaddr /ip4/127.0.0.1>

    # Decapsulate by protocol code
    m2 = Multiaddr("/ip4/192.168.1.1/tcp/8080/udp/1234")
    m2.decapsulate_code(6)  # TCP protocol code
    # <Multiaddr /ip4/192.168.1.1>

    # Decapsulate multiple layers
    m3 = Multiaddr("/ip4/10.0.0.1/tcp/443/tls/p2p/QmPeer")
    m3.decapsulate_code(6)  # Remove TCP and everything after
    # <Multiaddr /ip4/10.0.0.1>


Tunneling
---------

Multiaddr allows expressing tunnels very nicely.


.. code-block:: python

    printer = Multiaddr("/ip4/192.168.0.13/tcp/80")
    proxy = Multiaddr("/ip4/10.20.30.40/tcp/443")
    printerOverProxy = proxy.encapsulate(printer)
    print(printerOverProxy)
    # /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80

    proxyAgain = printerOverProxy.decapsulate(printer)
    print(proxyAgain)
    # /ip4/10.20.30.40/tcp/443

DNS Resolution
--------------

Multiaddr supports DNS-based address resolution using the DNSADDR protocol. This is particularly useful for resolving bootstrap node addresses and maintaining peer IDs during resolution.


.. code-block:: python

    from multiaddr import Multiaddr
    import trio

    # Basic DNS resolution
    ma = Multiaddr("/dns/example.com")
    resolved = await ma.resolve()
    print(resolved)
    # [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

    # DNSADDR with peer ID (bootstrap node style)
    ma_with_peer = Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
    resolved_with_peer = await ma_with_peer.resolve()
    print(resolved_with_peer)
    # [Multiaddr("/ip4/147.75.83.83/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")]

    # DNS4 and DNS6 resolution (IPv4/IPv6 specific)
    ma_dns4 = Multiaddr("/dns4/example.com/tcp/443")
    resolved_dns4 = await ma_dns4.resolve()
    print(resolved_dns4)
    # [Multiaddr("/ip4/93.184.216.34/tcp/443")]

    ma_dns6 = Multiaddr("/dns6/example.com/tcp/443")
    resolved_dns6 = await ma_dns6.resolve()
    print(resolved_dns6)
    # [Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946/tcp/443")]

    # Using the DNS resolver directly
    from multiaddr.resolvers import DNSResolver
    resolver = DNSResolver()
    resolved = await resolver.resolve(ma)
    print(resolved)
    # [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

    # Peer ID preservation test
    original_peer_id = ma_with_peer.get_peer_id()
    print(f"Original peer ID: {original_peer_id}")
    # Original peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

    for resolved_addr in resolved_with_peer:
        preserved_peer_id = resolved_addr.get_peer_id()
        print(f"Resolved peer ID: {preserved_peer_id}")
        # Resolved peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

For comprehensive examples including bootstrap node resolution, protocol comparison, and py-libp2p integration, see the `DNS examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/dns>`_ in the examples directory.

IP Filtering
------------

Accept/deny IP ranges for multiaddrs, similar to go-multiaddr ``Filters``:


.. code-block:: python

    from multiaddr import Action, Filters, Multiaddr

    filters = Filters()
    filters.add_filter("10.0.0.0/8", Action.DENY)
    assert filters.addr_blocked(Multiaddr("/ip4/10.1.2.3/tcp/80"))
    assert not filters.addr_blocked(Multiaddr("/ip4/8.8.8.8/tcp/53"))

See ``examples/filters/filters_example.py`` for a printable demo.

Socket dial arguments
---------------------

Convert thin-waist and unix multiaddrs to ``(network, address)`` pairs suitable
for ``socket.connect()``:


.. code-block:: python

    from multiaddr import Multiaddr, dial_args

    print(dial_args(Multiaddr("/ip4/1.2.3.4/tcp/80")))
    # ('tcp4', '1.2.3.4:80')
    print(dial_args(Multiaddr("/ip6/::1/tcp/80")))
    # ('tcp6', '[::1]:80')
    print(dial_args(Multiaddr("/unix/var/run/docker.sock")))
    # ('unix', '/var/run/docker.sock')

See ``examples/dial_args/dial_args_example.py`` for a printable demo.

Socket address conversion
-------------------------

Convert between Python socket address tuples and multiaddrs:


.. code-block:: python

    from multiaddr import from_net_addr, to_net_addr

    ma = from_net_addr(("1.2.3.4", 80))
    print(ma)
    # /ip4/1.2.3.4/tcp/80
    print(to_net_addr(ma))
    # ('1.2.3.4', 80)
    print(from_net_addr(("::1", 53), transport="udp"))
    # /ip6/::1/udp/53

See ``examples/net_addr/net_addr_example.py`` for a printable demo.

Thin Waist Address Validation
-----------------------------



.. code-block:: python

    from multiaddr import Multiaddr
    from multiaddr.utils import get_thin_waist_addresses, get_network_addrs, interface_multiaddrs

    # List local interfaces as Multiaddr objects
    for ma in interface_multiaddrs():
        print(ma)
        # Multiaddr('/ip4/192.168.1.10'), ...

    # Network interface discovery
    ipv4_addrs = get_network_addrs(4)
    print(f"Available IPv4 addresses: {ipv4_addrs}")
    # Available IPv4 addresses: ['192.168.1.12', '10.152.168.99']

    # Specific address (no expansion)
    addr = Multiaddr("/ip4/192.168.1.100/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.100/tcp/8080>]

    # IPv4 wildcard expansion
    addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/tcp/8080>, <Multiaddr /ip4/10.152.168.99/tcp/8080>]

    # IPv6 wildcard expansion
    addr = Multiaddr("/ip6/::/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip6/::1/tcp/8080>, <Multiaddr /ip6/fd9b:9eba:8224:1:41a1:8939:231a:b414/tcp/8080>]

    # Port override
    addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    result = get_thin_waist_addresses(addr, port=9000)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/tcp/9000>, <Multiaddr /ip4/10.152.168.99/tcp/9000>]

    # UDP transport support
    addr = Multiaddr("/ip4/0.0.0.0/udp/1234")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/udp/1234>, <Multiaddr /ip4/10.152.168.99/udp/1234>]

    # Server binding scenario
    wildcard = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    interfaces = get_thin_waist_addresses(wildcard)
    print("Available interfaces for server binding:")
    for i, interface in enumerate(interfaces, 1):
        print(f"  {i}. {interface}")
    # Available interfaces for server binding:
    #   1. /ip4/192.168.1.12/tcp/8080
    #   2. /ip4/10.152.168.99/tcp/8080

For comprehensive examples including error handling, practical usage scenarios, and detailed network interface information, see the `thin waist examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/thin_waist>`_ in the examples directory.

Features
========

- **Multiaddr Protocol Support**: Full support for the multiaddr specification
- **DNS Resolution**: Async DNS and DNSADDR resolution with trio
- **Thin Waist Validation**: Network interface discovery and wildcard expansion
- **Protocol Support**: IPv4, IPv6, TCP, UDP, DNS, DNS4, DNS6, DNSADDR, p2p, p2p-circuit, onion, onion3, quic, tls, and more
- **Type Safety**: Full type hints and mypy support
- **Modern Python**: Python 3.10+ support with modern tooling

Maintainers
===========

Original author: `@sbuss`_.

Current maintainers: `@acul71`_, `@pacrob`_, `@manusheel`_.

Contribute
==========

Contributions welcome. Please check out `the issues`_.

Check out our `contributing document`_ for more information on how we work, and about contributing in general.
Please be aware that all interactions related to multiformats are subject to the IPFS `Code of Conduct`_.

Development
-----------

For development setup, see `py-multiaddr contributing`_.

.. code-block:: bash

    # Clone the repository
    git clone https://github.com/multiformats/py-multiaddr.git
    cd py-multiaddr

    # Install in development mode
    pip install -e ".[dev]"

    # Run the development workflow
    make pr

License
=======

Dual-licensed:

-  `MIT`_ © 2014 Steven Buss
-  `Apache 2`_ © 2014 Steven Buss

.. _the issues: https://github.com/multiformats/py-multiaddr/issues
.. _contributing document: https://github.com/multiformats/multiformats/blob/master/contributing.md
.. _py-multiaddr contributing: https://github.com/multiformats/py-multiaddr/blob/master/CONTRIBUTING.rst
.. _Code of Conduct: https://github.com/ipfs/community/blob/master/code-of-conduct.md
.. _standard-readme: https://github.com/RichardLitt/standard-readme
.. _MIT: LICENSE-MIT
.. _Apache 2: LICENSE-APACHE2
.. _`@sbuss`: https://github.com/sbuss
.. _`@acul71`: https://github.com/acul71
.. _`@pacrob`: https://github.com/pacrob
.. _`@manusheel`: https://github.com/manusheel

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

sbuss

109 commits

acul71

53 commits

ntninja

40 commits

lla-dane

32 commits

multiformats/py-multiaddr

multiaddr implementation in Python

36

stars

369

commits

Python

primary language

Sep 6, 2026

updated

multiformats.io/
ipfs
libp2p
multiformats
network
python

README

py-multiaddr
==========================

.. image:: https://img.shields.io/pypi/v/multiaddr.svg
        :target: https://pypi.python.org/pypi/multiaddr

.. image:: https://github.com/multiformats/py-multiaddr/actions/workflows/ci.yml/badge.svg
        :target: https://github.com/multiformats/py-multiaddr/actions

.. image:: https://codecov.io/github/multiformats/py-multiaddr/coverage.svg?branch=master
        :target: https://codecov.io/github/multiformats/py-multiaddr?branch=master

.. image:: https://readthedocs.org/projects/multiaddr/badge/?version=latest
        :target: https://readthedocs.org/projects/multiaddr/?badge=latest
        :alt: Documentation Status
..

    multiaddr_ implementation in Python

.. _multiaddr: https://github.com/multiformats/multiaddr

..


.. contents:: :local:

Installation
============

.. code-block:: bash

    pip install multiaddr

Requirements
------------

- Python 3.10+
- trio (for async DNS resolution)

Usage
=====

CLI Tool
--------

py-multiaddr provides a command line interface to inspect multiaddrs and decode them into structured JSON.

.. code-block:: bash

    $ multiaddr /ip4/1.2.3.4/tcp/80
    {
      "string": "/ip4/1.2.3.4/tcp/80",
      "packed": "0x0401020304060050",
      "packedSize": 8,
      "components": [
        {
          "protocol": "ip4",
          "code": 4,
          "value": "1.2.3.4",
          "rawValue": "0x01020304"
        },
        {
          "protocol": "tcp",
          "code": 6,
          "value": "80",
          "rawValue": "0x0050"
        }
      ]
    }

You can also pass a hex-encoded multiaddr prefixed with ``0x``, e.g.
``multiaddr 0x0401020304060050``. Use the ``-c`` or ``--compact`` flag to
output on a single line.

IP Classification
-----------------

You can easily classify the IP components of multiaddrs (e.g. check for loopback, private networks, or NAT64):

.. code-block:: python

    from multiaddr import Multiaddr, is_private_addr, is_ip_loopback

    ma1 = Multiaddr("/ip4/192.168.1.1/tcp/80")
    print(is_private_addr(ma1))  # True
    print(is_ip_loopback(ma1))   # False

    ma2 = Multiaddr("/ip4/127.0.0.1")
    print(is_private_addr(ma2))  # True
    print(is_ip_loopback(ma2))   # True

Other available helpers include ``is_public_addr``, ``is_ip_unspecified``,
``is_ip6_link_local``, ``is_thin_waist``, and ``is_nat64_ipv4_converted_ipv6_addr``.
These operate on ``Multiaddr`` objects; ``is_wildcard`` / ``is_link_local_ip``
remain available for raw IP strings.

Simple
------

.. code-block:: python

    from multiaddr import Multiaddr

    # construct from a string
    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

    # construct from bytes
    #m2 = Multiaddr(bytes_addr=m1.to_bytes()) # deprecated
    m2 = Multiaddr(m1.to_bytes())

    assert str(m1) == "/ip4/127.0.0.1/udp/1234"
    assert str(m1) == str(m2)
    assert m1.to_bytes() == m2.to_bytes()
    assert m1 == m2
    assert m2 == m1
    assert not (m1 != m2)
    assert not (m2 != m1)


Protocols
---------

.. code-block:: python

    from multiaddr import Multiaddr

    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")

    # get the multiaddr protocol description objects
    m1.protocols()
    # [Protocol(code=4, name='ip4', size=32), Protocol(code=17, name='udp', size=16)]


En/decapsulate
--------------

.. code-block:: python

    from multiaddr import Multiaddr

    m1 = Multiaddr("/ip4/127.0.0.1/udp/1234")
    m1.encapsulate(Multiaddr("/sctp/5678"))
    # <Multiaddr /ip4/127.0.0.1/udp/1234/sctp/5678>
    m1.decapsulate(Multiaddr("/udp"))
    # <Multiaddr /ip4/127.0.0.1>

    # Decapsulate by protocol code
    m2 = Multiaddr("/ip4/192.168.1.1/tcp/8080/udp/1234")
    m2.decapsulate_code(6)  # TCP protocol code
    # <Multiaddr /ip4/192.168.1.1>

    # Decapsulate multiple layers
    m3 = Multiaddr("/ip4/10.0.0.1/tcp/443/tls/p2p/QmPeer")
    m3.decapsulate_code(6)  # Remove TCP and everything after
    # <Multiaddr /ip4/10.0.0.1>


Tunneling
---------

Multiaddr allows expressing tunnels very nicely.


.. code-block:: python

    printer = Multiaddr("/ip4/192.168.0.13/tcp/80")
    proxy = Multiaddr("/ip4/10.20.30.40/tcp/443")
    printerOverProxy = proxy.encapsulate(printer)
    print(printerOverProxy)
    # /ip4/10.20.30.40/tcp/443/ip4/192.168.0.13/tcp/80

    proxyAgain = printerOverProxy.decapsulate(printer)
    print(proxyAgain)
    # /ip4/10.20.30.40/tcp/443

DNS Resolution
--------------

Multiaddr supports DNS-based address resolution using the DNSADDR protocol. This is particularly useful for resolving bootstrap node addresses and maintaining peer IDs during resolution.


.. code-block:: python

    from multiaddr import Multiaddr
    import trio

    # Basic DNS resolution
    ma = Multiaddr("/dns/example.com")
    resolved = await ma.resolve()
    print(resolved)
    # [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

    # DNSADDR with peer ID (bootstrap node style)
    ma_with_peer = Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")
    resolved_with_peer = await ma_with_peer.resolve()
    print(resolved_with_peer)
    # [Multiaddr("/ip4/147.75.83.83/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN")]

    # DNS4 and DNS6 resolution (IPv4/IPv6 specific)
    ma_dns4 = Multiaddr("/dns4/example.com/tcp/443")
    resolved_dns4 = await ma_dns4.resolve()
    print(resolved_dns4)
    # [Multiaddr("/ip4/93.184.216.34/tcp/443")]

    ma_dns6 = Multiaddr("/dns6/example.com/tcp/443")
    resolved_dns6 = await ma_dns6.resolve()
    print(resolved_dns6)
    # [Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946/tcp/443")]

    # Using the DNS resolver directly
    from multiaddr.resolvers import DNSResolver
    resolver = DNSResolver()
    resolved = await resolver.resolve(ma)
    print(resolved)
    # [Multiaddr("/ip4/93.184.216.34"), Multiaddr("/ip6/2606:2800:220:1:248:1893:25c8:1946")]

    # Peer ID preservation test
    original_peer_id = ma_with_peer.get_peer_id()
    print(f"Original peer ID: {original_peer_id}")
    # Original peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

    for resolved_addr in resolved_with_peer:
        preserved_peer_id = resolved_addr.get_peer_id()
        print(f"Resolved peer ID: {preserved_peer_id}")
        # Resolved peer ID: QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN

For comprehensive examples including bootstrap node resolution, protocol comparison, and py-libp2p integration, see the `DNS examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/dns>`_ in the examples directory.

IP Filtering
------------

Accept/deny IP ranges for multiaddrs, similar to go-multiaddr ``Filters``:


.. code-block:: python

    from multiaddr import Action, Filters, Multiaddr

    filters = Filters()
    filters.add_filter("10.0.0.0/8", Action.DENY)
    assert filters.addr_blocked(Multiaddr("/ip4/10.1.2.3/tcp/80"))
    assert not filters.addr_blocked(Multiaddr("/ip4/8.8.8.8/tcp/53"))

See ``examples/filters/filters_example.py`` for a printable demo.

Socket dial arguments
---------------------

Convert thin-waist and unix multiaddrs to ``(network, address)`` pairs suitable
for ``socket.connect()``:


.. code-block:: python

    from multiaddr import Multiaddr, dial_args

    print(dial_args(Multiaddr("/ip4/1.2.3.4/tcp/80")))
    # ('tcp4', '1.2.3.4:80')
    print(dial_args(Multiaddr("/ip6/::1/tcp/80")))
    # ('tcp6', '[::1]:80')
    print(dial_args(Multiaddr("/unix/var/run/docker.sock")))
    # ('unix', '/var/run/docker.sock')

See ``examples/dial_args/dial_args_example.py`` for a printable demo.

Socket address conversion
-------------------------

Convert between Python socket address tuples and multiaddrs:


.. code-block:: python

    from multiaddr import from_net_addr, to_net_addr

    ma = from_net_addr(("1.2.3.4", 80))
    print(ma)
    # /ip4/1.2.3.4/tcp/80
    print(to_net_addr(ma))
    # ('1.2.3.4', 80)
    print(from_net_addr(("::1", 53), transport="udp"))
    # /ip6/::1/udp/53

See ``examples/net_addr/net_addr_example.py`` for a printable demo.

Thin Waist Address Validation
-----------------------------



.. code-block:: python

    from multiaddr import Multiaddr
    from multiaddr.utils import get_thin_waist_addresses, get_network_addrs, interface_multiaddrs

    # List local interfaces as Multiaddr objects
    for ma in interface_multiaddrs():
        print(ma)
        # Multiaddr('/ip4/192.168.1.10'), ...

    # Network interface discovery
    ipv4_addrs = get_network_addrs(4)
    print(f"Available IPv4 addresses: {ipv4_addrs}")
    # Available IPv4 addresses: ['192.168.1.12', '10.152.168.99']

    # Specific address (no expansion)
    addr = Multiaddr("/ip4/192.168.1.100/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.100/tcp/8080>]

    # IPv4 wildcard expansion
    addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/tcp/8080>, <Multiaddr /ip4/10.152.168.99/tcp/8080>]

    # IPv6 wildcard expansion
    addr = Multiaddr("/ip6/::/tcp/8080")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip6/::1/tcp/8080>, <Multiaddr /ip6/fd9b:9eba:8224:1:41a1:8939:231a:b414/tcp/8080>]

    # Port override
    addr = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    result = get_thin_waist_addresses(addr, port=9000)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/tcp/9000>, <Multiaddr /ip4/10.152.168.99/tcp/9000>]

    # UDP transport support
    addr = Multiaddr("/ip4/0.0.0.0/udp/1234")
    result = get_thin_waist_addresses(addr)
    print(result)
    # [<Multiaddr /ip4/192.168.1.12/udp/1234>, <Multiaddr /ip4/10.152.168.99/udp/1234>]

    # Server binding scenario
    wildcard = Multiaddr("/ip4/0.0.0.0/tcp/8080")
    interfaces = get_thin_waist_addresses(wildcard)
    print("Available interfaces for server binding:")
    for i, interface in enumerate(interfaces, 1):
        print(f"  {i}. {interface}")
    # Available interfaces for server binding:
    #   1. /ip4/192.168.1.12/tcp/8080
    #   2. /ip4/10.152.168.99/tcp/8080

For comprehensive examples including error handling, practical usage scenarios, and detailed network interface information, see the `thin waist examples <https://github.com/multiformats/py-multiaddr/tree/master/examples/thin_waist>`_ in the examples directory.

Features
========

- **Multiaddr Protocol Support**: Full support for the multiaddr specification
- **DNS Resolution**: Async DNS and DNSADDR resolution with trio
- **Thin Waist Validation**: Network interface discovery and wildcard expansion
- **Protocol Support**: IPv4, IPv6, TCP, UDP, DNS, DNS4, DNS6, DNSADDR, p2p, p2p-circuit, onion, onion3, quic, tls, and more
- **Type Safety**: Full type hints and mypy support
- **Modern Python**: Python 3.10+ support with modern tooling

Maintainers
===========

Original author: `@sbuss`_.

Current maintainers: `@acul71`_, `@pacrob`_, `@manusheel`_.

Contribute
==========

Contributions welcome. Please check out `the issues`_.

Check out our `contributing document`_ for more information on how we work, and about contributing in general.
Please be aware that all interactions related to multiformats are subject to the IPFS `Code of Conduct`_.

Development
-----------

For development setup, see `py-multiaddr contributing`_.

.. code-block:: bash

    # Clone the repository
    git clone https://github.com/multiformats/py-multiaddr.git
    cd py-multiaddr

    # Install in development mode
    pip install -e ".[dev]"

    # Run the development workflow
    make pr

License
=======

Dual-licensed:

-  `MIT`_ © 2014 Steven Buss
-  `Apache 2`_ © 2014 Steven Buss

.. _the issues: https://github.com/multiformats/py-multiaddr/issues
.. _contributing document: https://github.com/multiformats/multiformats/blob/master/contributing.md
.. _py-multiaddr contributing: https://github.com/multiformats/py-multiaddr/blob/master/CONTRIBUTING.rst
.. _Code of Conduct: https://github.com/ipfs/community/blob/master/code-of-conduct.md
.. _standard-readme: https://github.com/RichardLitt/standard-readme
.. _MIT: LICENSE-MIT
.. _Apache 2: LICENSE-APACHE2
.. _`@sbuss`: https://github.com/sbuss
.. _`@acul71`: https://github.com/acul71
.. _`@pacrob`: https://github.com/pacrob
.. _`@manusheel`: https://github.com/manusheel

Not written in Markdown, so it's shown here as plain text — view it formatted on GitHub.

Contributors

sbuss

109 commits

acul71

53 commits

ntninja

40 commits

lla-dane

32 commits

Languages

Python

98.4%

Makefile

1.6%