Summary
py-libp2p supports registering protocol stream handlers via set_stream_handler, but does not provide a guaranteed, system-wide way to unregister them.
This creates inconsistent service lifecycle behavior across modules.
Motivation
We need lifecycle symmetry for stream protocols in existing service lifecycle methods:
- service startup path should register handler(s)
- service shutdown path should unregister handler(s).
Without a standard unregister API, services currently use module-specific workarounds, which makes behavior inconsistent and harder to reason about on long-running hosts.
Current state
Core interfaces currently expose registration but not guaranteed removal:
IHost.set_stream_handler(...):
link
IMultiselectMuxer.add_handler(...):
link
No required remove_stream_handler / remove_handler contract exists at those interface levels.
Existing workaround patterns
-
Flags-only stop (service marked stopped, handler still registered)
-
Empty handler replacement as pseudo-unregister
- DCUtR sets an
empty_handler:
link
-
Optional unregister with fallback if method is missing
- Relay protocol tries
remove_stream_handler, falls back on AttributeError:
link
Proposed change (stream scope only)
- Add interface methods:
IHost.remove_stream_handler(protocol_id: TProtocol) -> None
IMultiselectMuxer.remove_handler(protocol: TProtocol) -> None
- Implement in core:
BasicHost.remove_stream_handler(...) delegates to multiselect
Multiselect.remove_handler(...) removes the protocol from handler map
- Service migration (follow-up in same or separate PRs):
- Update services that register handlers to unregister on stop/teardown
(perf, bitswap, kad_dht, relay/DCUtR, etc.)
Expected impact
- Consistent register/unregister semantics for stream protocols during service startup/shutdown
- Fewer ad-hoc unregister workarounds
- Cleaner behavior for long-lived hosts and protocol restarts
Are you planning to do it yourself in a pull request ?
yes
Summary
py-libp2psupports registering protocol stream handlers viaset_stream_handler, but does not provide a guaranteed, system-wide way to unregister them.This creates inconsistent service lifecycle behavior across modules.
Motivation
We need lifecycle symmetry for stream protocols in existing service lifecycle methods:
Without a standard unregister API, services currently use module-specific workarounds, which makes behavior inconsistent and harder to reason about on long-running hosts.
Current state
Core interfaces currently expose registration but not guaranteed removal:
IHost.set_stream_handler(...):link
IMultiselectMuxer.add_handler(...):link
No required
remove_stream_handler/remove_handlercontract exists at those interface levels.Existing workaround patterns
Flags-only stop (service marked stopped, handler still registered)
link
Empty handler replacement as pseudo-unregister
empty_handler:link
Optional unregister with fallback if method is missing
remove_stream_handler, falls back onAttributeError:link
Proposed change (stream scope only)
IHost.remove_stream_handler(protocol_id: TProtocol) -> NoneIMultiselectMuxer.remove_handler(protocol: TProtocol) -> NoneBasicHost.remove_stream_handler(...)delegates to multiselectMultiselect.remove_handler(...)removes the protocol from handler map(
perf,bitswap,kad_dht,relay/DCUtR, etc.)Expected impact
Are you planning to do it yourself in a pull request ?
yes