8000 golibdave: Ready() is false for ever when DAVE protocol version 0 is negotiated, so disgo sends and receives nothing on stage channels · Issue #11 · disgoorg/godave · GitHub
[go: up one dir, main page]

Skip to content

golibdave: Ready() is false for ever when DAVE protocol version 0 is negotiated, so disgo sends and receives nothing on stage channels #11

Closed

Description

@phpboyscout

Hey all! Same person as disgoorg/disgo#595 and disgoorg/disgo#604, back with another voice one. This time I think the cause is in golibdave, but I'd really value your read on whether I've understood the intent.

Full disclosure: like disgoorg/disgo#595, I investigated this with the help of an AI agent. I've checked what follows myself, live and in isolation.

What I was doing

I've been adding Stage channel support to a bot built on disgo and golibdave. Joining a stage works fine: the bot lands in the audience as you'd expect. But it never receives a single frame. The same build, in an ordinary voice channel a few minutes later, hears everything.

What I found

On a stage, the voice gateway's select-protocol ack comes back with dave_protocol_version: 0, and none of the DAVE opcodes follow. I'm guessing that's because stages aren't end-to-end encrypted: Discord's changelog lists E2EE for DMs, group DMs, voice channels and Go Live, but not stages.

With version 0, golibdave puts the encryptor into passthrough without a key ratchet, which looks right to me. But Ready() is

return !s.encryptor.IsPassthroughMode() && s.encryptor.HasKeyRatchet()

so it stays false for the whole connection. Since disgoorg/disgo#561, disgo's AudioSender and AudioReceiver both wait for Ready(), so nothing gets sent or read.

The part that made me think this isn't intended: Encrypt on that same session happily passes frames straight through. So the session already behaves as "no encryption here" and just doesn't say it's ready. The Session.Ready doc also says sessions that never establish E2EE "always return true". And on #8 you made the point that the stream shouldn't stop advancing when there's no E2EE group, which feels like exactly this situation.

Am I reading it right?

If there's a reason a disabled protocol should keep Ready() false, I'd love to understand it. If not, I have a small fix with tests ready on a fork. I'm happy to open it as a PR, or to rework it if you'd rather this was handled somewhere else, say in disgo or in how protocolInit treats version 0.

For what it's worth, I've worked around it on my side by reporting a disabled protocol as ready in a wrapper, and with that the stage delivers audio normally (around 625 frames in 20s from one speaker, where before it was 0).

Reproduction without Discord (golibdave v0.3.0)
s := golibdave.NewSession(logger, godave.UserID("1"), callbacks)
s.OnSelectProtocolAck(0)

frame := []byte{0xF8, 0xFF, 0xFE}
out := make([]byte, s.MaxEncryptedFrameSize(len(frame)))
n, err := s.Encrypt(1234, frame, out)

fmt.Println(s.Ready())                                 // false
fmt.Println(err == nil && bytes.Equal(out[:n], frame)) // true, passthrough works

With version 1 and no epoch yet, Ready() is false too, which is correct, so the difference is specifically the disabled case.

What the voice gateway sends on a stage

From disgo's debug log, decoded:

op 11  clients connect: [two users]
op 4   select protocol ack, dave_protocol_version = 0
op 5   speaking: <user>, ssrc ...      (someone is talking)
       no DAVE ops (21-31) follow, and AudioReceiver delivers nothing
The path through the code

OnSelectProtocolAck(0) → protocolInit(0) → the init transition runs with version 0 → setupKeyRatchetForUser(self, 0) sets passthrough and no key ratchet → Ready() is false for ever.

A downgrade to 0 by a later transition ends up in the same state once it executes.

Versions: godave and golibdave v0.3.0 (also checked on master at f34f982), libdave v0.3.0, disgo master at 68b7dbe6.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0