Problem
buffer.RawChannelWriter.Write automatically attempts bzip2 compression for every payload larger than 32 bytes. It tries up to three candidate lengths (len/1, len/2, and len/3) before falling back to an uncompressed MDU-sized stream message.
This matches the current Python RNS RawChannelWriter algorithm, but neither implementation exposes a supported way for a stream application to declare that its payload is already compressed or effectively incompressible. TLS, SSH, archives, media, encrypted application protocols, and generic TCP tunnels therefore pay the compression cost for almost every Channel message without saving wire bytes.
This is especially expensive because Buffer is a stream abstraction: the same failed compression probes are repeated throughout a long-lived byte stream.
Measured impact
Reticulum-Go v1.2.0, macOS arm64, Go 1.27.1, private Backbone/TCP -> Link -> Channel -> Buffer path on loopback, incompressible crypto/rand payload:
| workload |
elapsed / throughput |
| 1 MiB single stream |
2.392 s / 0.42 MiB/s |
| 10 MiB single stream |
17.540 s / 0.57 MiB/s |
| 512-byte request + 2048-byte response on an established Link |
6.314 ms per round trip |
| 10 concurrent x 1 MiB |
9.007 s total / 1.11 MiB/s aggregate |
The comparison transport on the same host and workload reaches 98-170 MiB/s for the single-stream rows. Link establishment is not the dominant cost: a fresh connection plus one-byte ping/pong is 344 microseconds.
A compression-only Python RNS 1.5.2 probe over 1 MiB of random data measured:
- 0.756 s when input is pre-fragmented to 457-byte payloads;
- 9.096 s with the reference writer shape, where each failed call probes up to 16 KiB but advances only one uncompressed MDU.
The performance problem is therefore not a Go-vs-Python wire behavior mismatch. It is inherited automatic-compression behavior that has no supported application policy control.
Compatibility requirement
Any solution must preserve full interoperability with Python RNS nodes:
- Do not change
StreamDataMessage wire encoding or compression flag semantics.
- Keep the existing automatic policy as the default so current Go behavior continues to match Python RNS.
- A sender that disables compression must emit the existing uncompressed
StreamDataMessage form (Compressed == false), which Python RNS already accepts.
- Receivers must continue to accept both compressed and uncompressed stream messages regardless of their local writer policy.
- Prefer an API shape that can also be introduced in Python RNS, with equivalent semantics and naming where practical. This should not create a Go-specific protocol dialect.
Proposed solution
Add an explicit writer compression policy, for example:
type CompressionPolicy uint8
const (
CompressionAuto CompressionPolicy = iota // current Python-compatible default
CompressionDisabled
)
type WriterOptions struct {
Compression CompressionPolicy
}
func NewRawChannelWriterWithOptions(streamID int, ch *channel.Channel, opts WriterOptions) *RawChannelWriter
The existing NewRawChannelWriter, CreateWriter, and CreateBidirectionalBuffer should retain CompressionAuto. Equivalent option-aware buffered constructors would let applications opt out without replacing the stock Buffer framing or defining a custom Channel message type.
Please add benchmarks covering:
- incompressible input;
- highly compressible input;
- mixed input;
- automatic vs disabled policy;
- small interactive writes and sustained multi-MiB streams.
It may also be worth discussing an adaptive policy later, but a deterministic opt-out is important for applications that already know the content characteristics.
Python RNS comparison
Python RNS master currently has the same three-probe bzip2 algorithm in RNS/Buffer.py. RawChannelWriter, create_writer, and create_bidirectional_buffer do not accept a compression option. RNS.Resource separately supports auto_compress=False, but that API does not provide Buffer stream semantics.
The fixed Go MaxDataLen vs Python's live channel.mdu is a separate parity/correctness issue already recorded in #17. This issue is intentionally scoped to compression policy and performance.
Alternatives considered
- Changing the default to no compression: rejected because it would diverge from Python RNS behavior and could regress low-bandwidth links.
- Changing the wire format: unnecessary and incompatible; the existing uncompressed stream-message representation is sufficient.
- Downstream monkey patches or custom stream message types: avoidable fragmentation of the ecosystem and not acceptable for applications that require stock Python-node interoperability.
- Using
Resource(auto_compress=false): not a replacement for a continuous bidirectional Buffer stream.
Problem
buffer.RawChannelWriter.Writeautomatically attempts bzip2 compression for every payload larger than 32 bytes. It tries up to three candidate lengths (len/1,len/2, andlen/3) before falling back to an uncompressed MDU-sized stream message.This matches the current Python RNS
RawChannelWriteralgorithm, but neither implementation exposes a supported way for a stream application to declare that its payload is already compressed or effectively incompressible. TLS, SSH, archives, media, encrypted application protocols, and generic TCP tunnels therefore pay the compression cost for almost every Channel message without saving wire bytes.This is especially expensive because
Bufferis a stream abstraction: the same failed compression probes are repeated throughout a long-lived byte stream.Measured impact
Reticulum-Go v1.2.0, macOS arm64, Go 1.27.1, private
Backbone/TCP -> Link -> Channel -> Bufferpath on loopback, incompressiblecrypto/randpayload:The comparison transport on the same host and workload reaches 98-170 MiB/s for the single-stream rows. Link establishment is not the dominant cost: a fresh connection plus one-byte ping/pong is 344 microseconds.
A compression-only Python RNS 1.5.2 probe over 1 MiB of random data measured:
The performance problem is therefore not a Go-vs-Python wire behavior mismatch. It is inherited automatic-compression behavior that has no supported application policy control.
Compatibility requirement
Any solution must preserve full interoperability with Python RNS nodes:
StreamDataMessagewire encoding or compression flag semantics.StreamDataMessageform (Compressed == false), which Python RNS already accepts.Proposed solution
Add an explicit writer compression policy, for example:
The existing
NewRawChannelWriter,CreateWriter, andCreateBidirectionalBuffershould retainCompressionAuto. Equivalent option-aware buffered constructors would let applications opt out without replacing the stock Buffer framing or defining a custom Channel message type.Please add benchmarks covering:
It may also be worth discussing an adaptive policy later, but a deterministic opt-out is important for applications that already know the content characteristics.
Python RNS comparison
Python RNS master currently has the same three-probe bzip2 algorithm in
RNS/Buffer.py.RawChannelWriter,create_writer, andcreate_bidirectional_bufferdo not accept a compression option.RNS.Resourceseparately supportsauto_compress=False, but that API does not provide Buffer stream semantics.The fixed Go
MaxDataLenvs Python's livechannel.mduis a separate parity/correctness issue already recorded in #17. This issue is intentionally scoped to compression policy and performance.Alternatives considered
Resource(auto_compress=false): not a replacement for a continuous bidirectional Buffer stream.