Companion receive filter
Overview
A filter in the companion firmware that keeps junk and unwanted channel messages from reaching the app, the web client and the touch UI. It is ported from the Dutch-MeshCore firmware and uses the same rule engine, list syntax and reply formats as their repeater packet filter, so tooling for one works for the other.
Hardware testing note: verified to build for the meshcomod companion envs. It has not yet been tested on hardware.
It filters on:
- Malformed group text (zero or implausible timestamp, empty text, invalid UTF-8)
- Message age (group text older than N minutes)
- Blocked path prefixes (anything relayed by one specific repeater)
- Sender name or message text of a group text (block, throttle, or dose)
The filter is disabled by default.
A build that contains the filter reports it in ver. A build.sh or CI build
replies with v1.18.0.1-deb2fba8-dmc-filter (Build: 16-Oct-2026) (firmware line,
commit, filter tag). The tag is only added to the ver reply; the version string
used by OTA and the flasher is unchanged.
Only flood traffic is filtered. Direct messages, and anything else addressed to this node, are never touched. Content checks run on the channels the companion holds the key for, which are exactly the channels the user sees.
The raw RX log (PUSH_CODE_LOG_RX_DATA) is not filtered. The radio sends it for
every packet it hears, before the filter runs. Apps that decode the raw log
themselves, such as RTFM-EV, still see filtered traffic; they need their own
rules to hide it.
Hidden or dropped
A companion normally does not repeat, so for most users every filtered message is simply not shown. The difference only matters with repeat enabled:
| Reason | Verdict | Shown in the app | Repeated (repeat on) |
|---|---|---|---|
| malformed, age, path | drop (junk) | no | no |
| sender, text | hide (personal) | no | yes |
Junk helps nobody, so it is not passed on. A sender or text rule is one user's taste: the message is hidden here, but still repeated for everyone else, so a mute list on a repeating companion never censors the mesh.
Command Overview
The commands run through the companion CLI: from the app's command console
(companion protocol CMD_RUN_CLI_COMMAND, v14+), over USB serial, or as remote
CLI from a contact that is allowed to use it.
filter
filter help
filter on
filter off
filter reset
filter malformed
filter malformed on|off
filter age
filter age <minutes>|off
filter path list
filter path add <hex>
filter path remove <hex>
filter sender list
filter sender add <name> [secs] [prob]
filter sender remove <name>
filter text list
filter text add <pattern> [secs] [prob]
filter text remove <pattern>
filter stats
filter stats malformed|age|path|sender|text
Every change is saved immediately.
Enabling the Filter
filter on
> Filter: on
filter off keeps every setting but filters nothing. filter reset restores the
defaults (everything off, all lists empty); the counters are not affected.
Malformed Group Text
filter malformed on
> Filter: malformed scan on
Drops group text whose timestamp is zero, or more than a week off this node's clock (only once the clock has been set), and plain text that is empty or not valid UTF-8. These are the same checks as the repeater filter, but on every channel the companion holds rather than on Public only.
Message Age Limit
filter age 60
> Filter: OK
Drops group text stamped more than this many minutes ago (1-10080, or off).
Stale messages are what a misbehaving node replays hours later. Inactive while
this node's clock is not set; companions usually get the time from the app.
Blocked Path Prefixes
filter path add A1B2
> Filter: path A1B2 added
Drops flood adverts, group text and group data whose path contains a repeater ID starting with the prefix (2-8 hex digits, up to 8 prefixes). Use it when one repeater keeps injecting junk. A prefix longer than the packet's path hash size never matches. Note that a blocked advert also no longer refreshes an existing contact.
Sender and Text Rules
filter sender add Bot*
> Filter: sender Bot* added
filter text add ^!ping 300
> Filter: text ^!ping added
Up to 8 sender rules and 8 text rules, checked top to bottom; the first rule that decides hides the message.
- Sender: exact name, or a prefix when it ends in
*. Case-sensitive. - Text: substring, or the start of the message when it begins with
^. - secs (optional):
0(default) hides every match. Otherwise one match persecsseconds is shown and the rest are hidden, which slows a chatty bot down instead of muting it. - prob (optional, 1-100, default 100): the share of matches the rule decides; the rest go on to the next rule.
filter sender list / filter text list show the rules:
filter text list
^!ping throttle 300s
Statistics
filter
> Filter on: Hidden [ Malformed: 3 | Age: 0 | Path: 12 | Sender: 41 | Text: 7 ]
Per topic:
| Command | Reply |
|---|---|
filter stats malformed |
> Blocked 3 [ short:0 time:2 empty:0 utf8:1 ] |
filter stats age |
> Message age: max 60m, dropped 0 (or > Filter: message age limit off) |
filter stats path |
A1B2: 12 per prefix, or None |
filter stats sender / text |
Bot*: 41 per rule, with (pass N) for throttle passes |
Counters live in RAM and restart at zero after a reboot. A message is counted once, even when several repeaters relay it.
Persistent Configuration
Settings are stored as JSON in /cfilter.json, next to the other data files (on
the SD card under /meshcomod when SD storage is in use). A missing file means defaults
(off). Out-of-range values in a hand-edited file are clamped on load.
Error Replies
| Reply | Cause |
|---|---|
> Filter: command error |
unknown subcommand |
> Filter: syntax error 'filter malformed <on \| off>' |
bad malformed argument |
> Filter: error <minutes> range is 1-10080, or off |
bad age argument |
> Filter: error path prefix is 2-8 hex digits |
bad path prefix |
> Filter: error <pattern> max N chars |
sender pattern over 15, text pattern over 23 characters |
> Filter: error <secs> range is 0-65535, <prob> range is 1-100 |
bad rule arguments |
Failed |
duplicate entry, list full, or nothing to remove |