Appearance
Transform pipeline
Each interface carries two ordered lists of transforms: one applied to messages coming in, one to messages going out. ingress_transforms and egress_transforms on the interface record them, and a sequence number tracks their order.
Why per interface and per direction
A transform belongs to the link, not to the message. Compressing before a satellite hop is obviously right; compressing before writing to a local MQTT broker is wasted work. Encrypting on the way out to a shared bearer is right; encrypting on the way in from a trusted serial port is not. Attaching the pipeline to the interface and the direction is what lets those be different.
Order matters, and it is enforced
Configure the outbound chain to compress, then encrypt. The receiving chain must undo those operations in reverse: decrypt, then decompress, using the matching key and compression dictionary. Trying to decompress ciphertext will not recover the original message.
Compression needs structure in the input; encryption removes that structure. Both ends need compatible transform settings, not just the same transport.
Compression
Text is compressed with SMAZ2 against the Meshtastic vocabulary. The dictionary is byte identical across the Bridge, the Hub and the Android app, because a message compressed by one is decompressed by another and a dictionary mismatch is a silent decode failure rather than an error.
Typical gains are large on short human text and negligible on binary, which is why the pipeline is configurable rather than always on.
Encryption
AES-256-GCM, wire format [12 byte nonce][ciphertext + 16 byte tag]. Identical across all three products for the same reason the dictionary is.

