Skip to content

feat: add CLM request for channel level list (message ID 1025) - #3725

Open
mcfnord wants to merge 6 commits into
jamulussoftware:mainfrom
mcfnord:feature/clm-req-channel-level-list
Open

feat: add CLM request for channel level list (message ID 1025)#3725
mcfnord wants to merge 6 commits into
jamulussoftware:mainfrom
mcfnord:feature/clm-req-channel-level-list

Conversation

@mcfnord

@mcfnord mcfnord commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

🤖 AI: Adds PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST (ID 1025): a new connectionless message that causes a server to immediately reply with the existing PROTMESSID_CLM_CHANNEL_LEVEL_LIST (ID 1015) response.

This follows the established CLM request/response pattern used by:

  • CLReqConnClientsList (1014) → CLConnClientsList (1013)
  • CLReqVersionAndOS (1012) → CLVersionAndOS (1011)
  • CLReqServerList (1007) → CLRedServerList (1018) and CLServerList (1006)

Numbered 1025: the CLM block runs 1001 to 1022 with no gaps, and 1023 and 1024 are taken by #3636.

The name is not new. The protocol documentation in protocol.cpp has referred to PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST since a7a1549 (2020), at lines 437 and 440, for a message that was never defined. Message 28 is its connected-mode counterpart: CClient::OnNewConnection still sends it, and it is kept only for servers 3.4.6 to 3.5.12.

Implementation

4 files, 28 lines:

  • protocol.h — define ID 1025, declare evaluator, declare signal
  • protocol.cpp — document the message in the reference block, add switch case, and one-line EvaluateCLReqChannelLevelListMes that emits the signal
  • server.h — one-line OnCLReqChannelLevelList slot that calls the existing CreateCLChannelLevelListMes
  • server.cpp — connect signal to slot

Zero additional computation. vecChannelLevels is already computed every server tick in OnTimer(). The handler simply forwards that existing data to the requester.

Use case

A monitoring tool can poll the activity level on any Jamulus server by sending a single UDP datagram to the server's game port and reading the nibble-packed level list in the 1015 response. No audio connection, no additional open ports, no configuration required.

The 1015 response already encodes the number of connected clients implicitly (the list length), so the caller can detect idle servers (no clients) versus quiet ones (clients present but all levels ≤ threshold).

Relation to prior PR

This supersedes #3724.


🤖 This message was written by AI and reviewed by @mcfnord.

Adds PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST (1028): a connectionless
request that causes the server to immediately reply with the existing
PROTMESSID_CLM_CHANNEL_LEVEL_LIST (1015) response.

This follows the established CLM request/response pattern used by
CLReqConnClientsList (1014 → 1013), CLReqVersionAndOS (1012 → 1011),
and CLReqServerList (1007 → 1002).

The implementation is zero-cost: vecChannelLevels is already computed
every server tick in OnTimer(). The handler simply forwards that existing
data to the requesting address.

Use case: external monitors can poll silence/activity level on any
Jamulus server by sending a single UDP datagram to the server's game
port, without establishing an audio connection or requiring additional
open ports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@ann0see ann0see left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way better!

@pljones

pljones commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator

Yes, much better!

@mcfnord

mcfnord commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

It says there are branch conflicts that must be resolved, and it says there's a C-like coding style failure.

I can try to fix those if it's inherently a PR you'd merge.

I've found a strong case for the feature, but I plan to try accomplishing similar results on a very lean use of message 28, as recommended here but drawing criticism here.

@ann0see

ann0see commented Jun 21, 2026

Copy link
Copy Markdown
Member

If the usecase is beneficial and a simple approach, it would not be rejected by me. However, you want to detect bots/listeners? Then please make clear why you need this. Do we want the client to mark those clients? Does it have any information leakage/privacy concerns?

@mcfnord

mcfnord commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

you want to detect bots/listeners? Then please make clear why you need this.

Yes, I want to identify silent connections. Maybe it's a listener, maybe it's a bot that doesn't actually listen, but the thing important to me is: it's not a signal in the sense that we, an audio platform, think about signals.

I use this data in an immediate sense, where it detects someone who maybe has stayed connected while walking away for a while, and also in a long-term sense where I can determine that account xyz is always silent and might not be an active musician (so a perma-listener or a bot).

Do we want the client to mark those clients?

Maybe! https://jamulus.live marks server cards with the "no sound" emoji (🔇) when I determine that every connection on the server is silent. This would be a helpful navigation aide in the Connection dialog. It could be per-connection, although that seems overwhelming. So maybe it just shows that the whole server is silent at the moment.

Notice how people roam around looking for activity... they end up popping in on small groups or singletons to determine if they're active connections. With the "no sound" emoji, I can look at 5 small groups on 5 servers, and see that one is actually making sound. This directs users to the active connection. If the Jamulus client indicated silence this way, it would have the same beneficial effect on navigation.

Does it have any information leakage/privacy concerns?

I can't think of any. It's just a bit, silent or not-silent. Doesn't present an audio sample or additional metadata. It reveals "level of audio" which might have some application somehow, but I can't think of a malicious use for that detail.

I have added this feature to my own server binary and deployed it to about 20 Jamulus servers. I also get the same information through existing connected lounge lobby bots (mostly Thailand). It provides a helpful clue that the server is silent.

@pljones

pljones commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

This directs users to the active connection. If the Jamulus client indicated silence this way, it would have the same beneficial effect on navigation.

Not always a beneficial impact, to be fair. Five drummers, four bass players, three guitarists, flute, violin, tabor and harp - and still no singer... They may not be silent but they'd probably make a better stab at fun if they split up a bit. In my view.

@mcfnord mcfnord mentioned this pull request Jul 17, 2026
4 tasks
@ann0see

ann0see commented Jul 17, 2026

Copy link
Copy Markdown
Member

I also thought about having a bot instrument... (I know it's only partly related)

@pljones pljones added this to the Release 4.1.0 milestone Jul 19, 2026
@pljones pljones added this to Tracking Jul 19, 2026
@github-project-automation github-project-automation Bot moved this to Triage in Tracking Jul 19, 2026
@dingodoppelt dingodoppelt added the AI AI generated or potentially AI generated label Jul 28, 2026
@ann0see
ann0see self-requested a review August 9, 2026 14:10
@mcfnord

mcfnord commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

This would be cool, let's merge this.

@ann0see
ann0see requested a review from dingodoppelt September 1, 2026 16:19
Comment thread src/protocol.h Outdated
#define PROTMESSID_CLM_REGISTER_SERVER_RESP 1016 // status of server registration request
#define PROTMESSID_CLM_REGISTER_SERVER_EX 1017 // register server with extended information
#define PROTMESSID_CLM_RED_SERVER_LIST 1018 // reduced server list
#define PROTMESSID_CLM_REQ_CHANNEL_LEVEL_LIST 1028 // request channel level list (connectionless)

@ann0see ann0see Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit weird to have a jump to 1028 here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed -- next sequential is 1023 and should be added sequentially.

@mcfnord mcfnord Sep 2, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI: I figured if the connected-mode message is 28, then the connectionless one could be 1028. But 1025 is alright, too. Not 1023, though: #3636 already takes 1023 for PROTMESSID_CLM_TCP_OFFERED and 1024 for PROTMESSID_CLM_CLIENT_ID, so 1025 is the next one actually free.

@github-project-automation github-project-automation Bot moved this from Triage to Waiting externally in Tracking Sep 1, 2026
1023 and 1024 are taken by the open TCP PR jamulussoftware#3636
(PROTMESSID_CLM_TCP_OFFERED, PROTMESSID_CLM_CLIENT_ID), so 1025 is the
lowest CLM ID free on main and unclaimed by any open PR. The define also
moves to the end of the CLM block so the list stays in numeric order.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019KZHjFTo73t39UURauvXvg
@mcfnord mcfnord changed the title feat: add CLM request for channel level list (message ID 1028) feat: add CLM request for channel level list (message ID 1025) Sep 2, 2026
Every other connectionless message has an entry in the protocol
documentation block at the top of protocol.cpp; this one did not. The
entry follows the PROTMESSID_CLM_REQ_CONN_CLIENTS_LIST form: no payload,
plus the one line of behaviour the handler implements (one
PROTMESSID_CLM_CHANNEL_LEVEL_LIST back to the requesting address).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019KZHjFTo73t39UURauvXvg
@ann0see

ann0see commented Sep 3, 2026

Copy link
Copy Markdown
Member

Actually - after thinking a bit more, I think that we should allow the server admin disable this and expose via the server capabilities if this feature is enabled/disabled for privacy reasons. Default enabled.

Maybe with --blacklist-messages/--blacklist-features '' which can then be extended in future?

@dingodoppelt

Copy link
Copy Markdown
Member

Actually - after thinking a bit more, I think that we should allow the server admin disable this and expose via the server capabilities if this feature is enabled/disabled for privacy reasons. Default enabled.

Maybe with --blacklist-messages/--blacklist-features '' which can then be extended in future?

I second that. Furthermore I'd like to repeat that we might want to log requests like this so admins can monitor activity on their servers and decide whether to block or allow such traffic.

Comment thread src/protocol.h Outdated
Comment thread src/server.cpp Outdated
Comment thread src/server.h Outdated
…ing the #define order

Review asked that the new message keep the same position as its #define
(the last entry in the CLM block) everywhere it appears, and that the
connect() block in server.cpp keep one blank line between entries. This
moves the Evaluate declaration and the signal in protocol.h, the switch
case and the Evaluate definition in protocol.cpp, the connect() in
server.cpp, and the slot in server.h to follow the
PROTMESSID_CLM_REQ_WELCOME_MESSAGE entry in each block. No behaviour
change: a headless build of this commit answers a 1025 request with one
1015 reply and still answers 1014 with 1013.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S8KACM96jgMTGCLbqqta9Z
@mcfnord

mcfnord commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 AI: @ann0see — the control belongs with the user, not the operator.

An operator who wants to refuse this traffic already has the right layer for it — the kernel: firewall and packet filtering, outside the application, where a host's policy over its own machine sits. A --blacklist-features flag makes the operator the arbiter of whether a user's signal is shared, and that is the user's call to make.

So the gate, if there is one, is a client checkbox: the user opts out, the client carries the choice, the server honors it in the level reply — the decision staying with the person the signal describes.

It also scopes cleanly. CLM_CHANNEL_LEVEL_LIST has two emitters: the in-session broadcast that drives the mixer meters, and the connectionless reply this PR requests. A user opt-out applies only to the connectionless reply — the navigation view — so a hidden user still shows a live meter to the people they are playing with; only the anonymous query loses them.

This is a separate feature from the request itself, which stays as it is.

@ann0see

ann0see commented Sep 4, 2026

Copy link
Copy Markdown
Member

This is a separate feature from the request itself, which stays as it is.

Yes - but the privacy implications should be solved before this gets merged.

@mcfnord

mcfnord commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

In your view, does unregulated disclosure of name/instrument/nation present the same concerns?

@ann0see

ann0see commented Sep 4, 2026

Copy link
Copy Markdown
Member

No since the user sets them willingly or not.

@ann0see

ann0see commented Sep 4, 2026

Copy link
Copy Markdown
Member

Server side blocking is easier. This I proposed this.

@dingodoppelt

Copy link
Copy Markdown
Member

An operator who wants to refuse this traffic already has the right layer for it — the kernel: firewall and packet filtering, outside the application, where a host's policy over its own machine sits. A --blacklist-features flag makes the operator the arbiter of whether a user's signal is shared, and that is the user's call to make.

So the gate, if there is one, is a client checkbox: the user opts out, the client carries the choice, the server honors it in the level reply — the decision staying with the person the signal describes.

I totally disagree here. In my perception internal data mustn't be shared. I still don't like this feature and I think we shouldn't merge this at all because it is actually not even a Jamulus feature. It's a third-party app feature.

@ann0see

ann0see commented Sep 4, 2026

Copy link
Copy Markdown
Member

I totally disagree here. In my perception internal data mustn't be shared.

Yes. One could imagine that it's possible to infer parts of conversations or similar from it. If that's possible, users would never know that this happened.

It's a third-party app feature.

Yes. But we should still be somewhat nice to external apps. A human - be it the server admin or enduser should be able to decide what kind of data he/she shares -> needs blacklisting or whitelisting.

@mcfnord

mcfnord commented Sep 5, 2026

Copy link
Copy Markdown
Contributor Author

It should be a Jamulus feature. A silent gathering should show it's silent.

@pljones

pljones commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

It's a third-party app feature.

I think this is an important point.

I was against opening up Jamulus to JSON-RPC on this basis. It was, eventually, agreed that Jamulus itself would only support exposing the current state of the Client and the Server locally via JSON-RPC. (This still doesn't protect a Client user from having information exposed without their knowledge, of course, and the Server operator can then expose everything if they proxy the information out to the wider internet by whatever means.)

Of course, it's always been possible to request information about Servers and Clients using the existing (connectionless) protocol messages - but these are the same ones, exposing the same information, as expose that information to all Jamulus users.

We recently added the welcome message connectionless request, information which previously required a connection. I can see the use of it. It seems harmless enough...

...But it then does open up the floor to requests for other state information to be passed to unknown remote servers without Client or Server operator intervention, that Client users currently think of as known only to others within the session they're connected to.

Is it a line that should be held or crossed?

("Ghost probes" can, of course, already retrieve any connected information -- if there is a slot available on the Server. That was why the welcome message request was allowed. It appeared to be a legitimate need to retrieve information about the Server itself, rather than connection-oriented session state.)

@ann0see

ann0see commented Sep 5, 2026

Copy link
Copy Markdown
Member

@mcfnord. Please exactly describe which problem you want to solve then on your app. Is it finding servers which are just silent such that you can hide them?

@pljones

pljones commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

I mean, we could add "minutes since overall server sound level went above -48dB" to the connect dialog. That's enough to tell if the server is silent without publishing every Client level (and needing to sample it frequently to have it make any sense). (A tiny thread similar to the recorder could take a push of the levels, do the maths and signal back the sum every so often. Clients with an extended server list poll could have this included... we do need a more extensible format... let's switch to a JSON blob.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI AI generated or potentially AI generated

Projects

Status: Waiting externally

Development

Successfully merging this pull request may close these issues.

4 participants