Skip to content

feat: CLI tool to decode multiaddrs into structured JSON #119

Description

@sumanjeet0012

Summary

py-multiaddr has no CLI for inspecting multiaddrs. go-multiaddr ships a multiaddr binary that decodes a multiaddr (string or hex-packed bytes) into structured JSON, which is useful for debugging, scripting, and cross-implementation testing. Python users currently have to write a throwaway script and import the library just to see what a multiaddr decodes to.

Motivation

  • Debugging: Quickly inspect an opaque multiaddr string or hex blob from logs without spinning up a Python shell.
  • Interop testing: Compare Python's decoding of a multiaddr against go-multiaddr's output for the same input, which is valuable for the multiaddr spec conformance work across implementations.
  • Scripting/tooling: Other CLI tools (shell scripts, CI checks) can pipe a multiaddr string into this and consume JSON output rather than parsing custom text.
  • Parity: Every other major multiaddr implementation (Go, at least) exposes this developer convenience; Python is the odd one out.

Current Behavior

There's no multiaddr executable and no documented way to get a structured, per-protocol breakdown of a multiaddr from the command line.

Proposed Solution

Add a multiaddr/cli.py module and register it as a console script:

[project.scripts]
multiaddr = "multiaddr.cli:main"

The CLI should:

  1. Accept a multiaddr as either a string (/ip4/1.2.3.4/tcp/80) or hex-packed bytes (0x...).
  2. Decode it and print JSON containing:
    • the canonical string form
    • the packed byte size
    • the packed hex representation
    • a components array, one entry per protocol segment, each with protocol name, code, decoded value, and raw hex value

Example (matching go-multiaddr's output shape)

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

A draft implementation is sketched below (needs review against the actual internal API — bytes_iter/transforms naming may not match current internals):

import argparse
import json
from .multiaddr import Multiaddr
from .transforms import bytes_iter
 
def main():
    parser = argparse.ArgumentParser(description="Inspect multiaddrs")
    parser.add_argument("addr", help="Multiaddr string or hex bytes (0x...)")
    args = parser.parse_args()
 
    addr = Multiaddr(bytes.fromhex(args.addr[2:])) if args.addr.startswith("0x") else Multiaddr(args.addr)
 
    components = [
        {
            "protocol": proto.name,
            "code": proto.code,
            "value": codec.to_string(proto, value) if codec.SIZE != 0 else None,
            "rawValue": "0x" + value.hex(),
        }
        for offset, proto, codec, value in bytes_iter(addr.to_bytes())
    ]
 
    print(json.dumps({
        "string": str(addr),
        "packed": "0x" + addr.to_bytes().hex(),
        "packedSize": len(addr.to_bytes()),
        "components": components,
    }, indent=2))

Open Questions / Acceptance Criteria

  • Should invalid input produce a JSON error object (for scripting) or a human-readable stderr message + non-zero exit code? (Go's behavior should be checked for consistency.)
  • Should output be pretty-printed by default with a --compact/-c flag for single-line JSON?
  • Should there be a --version flag reporting the installed multiaddr package version?
  • Add tests comparing CLI output against known-good fixtures (ideally shared/ported from go-multiaddr's test vectors).
  • Document the CLI in the README.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions