Skip to main content
Everything exported from @cloak-software/bot-sdk for voice. For the concepts behind these, start at Voice overview.
The interface behind client.voiceKeys is called VoiceKeyAccess in the source and is not exported. Do not write an import for it. Reach the lane through client.voiceKeys.

Client surface

These live on Client, not in the voice modules. Documented in full on Joining voice. client.guild(serverId) binds joinVoice(channelId, opts?) and refreshVoiceRoster().

voiceConnectionReporter

The connection-state sender a media session needs. Pass the result as VoiceMediaOptions.connectionReporter. The returned function sends the bot’s own media connection state to the backend (0 up, 1 reconnecting); on a current backend the 0 a server-voice session sends before it publishes is what admits the microphone. It rides the send chain, is a no-op when the bot is not in voice or is in a DM call, and never throws or rejects. Sending it twice is harmless. See Microphone admission. Events: voiceJoin, voiceLeave, voiceStateUpdate, voiceConnectionState, voiceDisconnect, voiceKeyUpdate. See Events.

livekitUrlFor

The media signalling URL for a Cloak deployment, derived from the transport URL: same host, port 7880, wss: (or ws: when the transport itself is insecure). Returns '' for an unparseable URL. This is what VoiceConnection.url defaults to. Override it with ClientOptions.livekitUrl. The SDK never connects to that URL. It is passed through for the media layer.

Types

VoiceConnection

token is a live credential. Never log it or put it in an error message.

VoiceParticipant

p2pWebcam and p2pStream are dead API. Peer-to-peer video has been removed from the platform. The fields remain so the wire column map stays stable, and they are permanently false.
serverMuted and serverDeafened are best-effort in the initial roster snapshot: the backend appends them from a separate query whose row order is not guaranteed to line up. The delta events correct them.

VoiceKey

VoiceKeyScope

Read from an authenticated tag inside the sealed key, never computed and never defaulted. See Voice keys and scopes.

VoiceStateFlag

The state field on voiceStateUpdate. The last two are deprecated and never change.

VoiceMediaSession

Requires the optional native engine. See Voice runtime.
Promise<VoiceMediaSession>
Connects, installs frame keys, reports the connection up through connectionReporter and waits for the microphone admission, then publishes one microphone track. Fails closed on every precondition. Full walkthrough on Publishing and receiving audio.
string
The identity the SFU knows this bot by. Asserted equal to the normalized bot id at attach time.
'participant' | 'room'
The scope the key arrived with. Read-only.
readonly string[]
Every identity this session has filed a key for. In participant scope this is exactly one, the bot’s own.
Promise<void>
Re-pin after a voiceKeyUpdate. Detaches the session and throws when the new scope differs from the attached one.
Promise<void>
Stops the player, unpublishes, and disconnects. Idempotent. Ends every receive iterable with a normal return.
this
Room scope only. Fires once per subscribed remote audio track with a ParticipantAudio. A re-subscribe for the same identity is a new event with a new iterable. Throws in participant scope: that session holds only its own frame key and can decrypt nobody. off() removes a listener.
AsyncIterable<ParticipantFrame>
Room scope only. Every live participant’s frames merged into one iterable, each tagged with its identity. Later arrivals join, leavers drop out, and any number of concurrent consumers each see every frame. Ends with a normal return on detach(). return() (a break) releases every underlying stream this consumer held. Throws in participant scope.
Promise<void>
Room scope only. Subscribe or unsubscribe this bot from one identity’s audio tracks. false ends that identity’s live iterable immediately with a normal return; a later true produces a fresh audio event. Rejects when no participant with that identity is in the room. Throws in participant scope.
Security, in full: a bot without voice_listen holds only its own frame key and is cryptographically unable to decrypt any member; a bot granted voice_listen can decrypt everyone in the channel, by design. See Receiving audio.

VoiceMediaOptions

receiveSampleRate and receiveChannels set the format of every received PcmFrame. They are fixed at attach, and the engine does the resampling and channel mixing. connectionReporter is not optional in practice. Pass client.voiceConnectionReporter(). attach() calls it with 0 before the publish, once the identity and key checks have passed, then waits for the microphone admission by polling the local participant’s canPublish permission every 100 ms and re-sending the report every second, up to admissionTimeoutMs. It also calls it with 1 then 0 around an engine reconnect, and never from detach(). Without it there is no wait and a server-voice credential stays microphone-clamped on a current backend, so the publish is silent, with a warning only under debug. admissionTimeoutMs bounds that wait. Default 5000. On timeout attach() still publishes once, because an older single-phase backend never flips the flag but its credential already allows publishing; if that publish rejects, attach() fails closed with an error naming the two-phase admission, connectionReporter, and admissionTimeoutMs. Ignored without a connectionReporter. See Microphone admission.

VoiceMediaSessionEvents

PcmFrame

One received frame of decrypted PCM, already resampled to the session’s receiveSampleRate and receiveChannels. data is interleaved signed 16-bit, samplesPerChannel * channels long, and a copy out of the engine, so it is safe to keep after the next frame arrives. Consumers sharing a session see the same data: read it or convert it, never write into it.

ParticipantAudio

What the audio event carries. identity is the participant’s normalized Cloak user id. frames ends with a normal return when that track is unsubscribed or the session detaches. A muted track yields nothing and stays open. More than one consumer may iterate it, and each sees every frame. A consumer slower than real time is capped at 500 queued frames before the oldest is dropped.

ParticipantFrame

What session.audio() yields.

VoiceKeyValue

A voice key exactly as the key lane hands it over. VoiceKey satisfies it structurally.

VoiceFrameKeySource

The one seam the media session gets frame keys from. client.voiceKeys satisfies it. It exists so the publish path never sees a scope.

VoicePlayer

You get one on session.player. Constructing your own is only for testing.
Promise<void>
Pumps a resource to completion. Resolves when the resource ends or stop() is called. Rejects when the source throws, including a non-zero decoder exit. Throws immediately if a resource is already playing.
void
Ends the current resource. The in-flight frame is still awaited.

VoicePlayerEvents

VoicePlayerDeps

Injected rather than imported, so the player is testable with no native code on disk.

Audio resources

AudioResource

Interleaved signed 16-bit PCM. Each chunk must contain a whole number of frames. close() is called once when the pump ends for any reason, including stop() and errors.

pcmChunks

Adapts a byte stream to frame-aligned chunks. Handles chunks that end mid-sample and buffers whose offset is not two-byte aligned. A trailing partial frame is dropped.

pcmFrameBytes

Serializes one received PcmFrame as interleaved little-endian signed 16-bit bytes: the inverse of pcmChunks, and the layout ffmpeg -f s16le, a WAV data chunk, and raw-PCM speech-to-text inputs expect. Pure and endian-fixed. It copies exactly samplesPerChannel * channels samples and never aliases frame.data. This is the whole of what the SDK does with received audio. There is no recorder, no file sink, and no transcription helper, on purpose. See Receiving audio.

ffmpegResource

Decodes a file path or a direct media URL. Synchronous. Throws a CloakEnvironmentError at play time when the decoder cannot start or exits non-zero.

ytdlpResource

Resolves a streaming-site page URL with yt-dlp, then decodes it with ffmpeg. Async, unlike ffmpegResource.
--no-playlist is always passed.

resolveMediaUrl

Resolves a page URL to a direct media URL and returns it, so you can log, cache, or inspect it yourself.
The result is signed, expiring, and usually bound to this host’s IP address. Resolve at play time. Do not persist it and do not hand it to another machine.

DecoderChild, DecoderSpawn, ExtractorRun

Test seams for the decoder and extractor plumbing, exported so the failure paths are reachable offline. DecoderChild is a narrow structural view of a spawned process. Not version-stable.

Runtime

probeVoiceRuntime

Tries to load the engine and reports what happened. Never throws.

voiceEnvError

Maps an engine load failure onto an actionable error. Recognises old glibc, musl hosts, and a missing package, and attaches a remedy naming what to change on the machine.

Runtime constants

Encryption

These are a cross-platform contract shared with the Cloak desktop, web, and mobile clients. Treat them like a wire format, not like configuration.

deriveParticipantVoiceKey

Derives one participant’s key from the room key. Throws unless roomKey is exactly 32 bytes. Derives downward only: there is no inverse, and that absence is what makes a publish-only bot structurally deaf. Pinned by a golden test vector shared across repos.

voiceKeyMaterial

Asserts a key is exactly 32 bytes and returns it. A 33-byte value gets its own error message: the sealed plaintext is a 1-byte scope tag followed by 32 key bytes, and a 33-byte value here means the tag was never stripped. Slicing it off at this point would install a byte-shifted key that the provider accepts without complaint and that no peer can decrypt.

voiceKeyProviderOptions

The frame-key provider options the SDK configures the engine with. Exported so the contract is inspectable. There is deliberately no shared key and no shared-key field: per-participant mode is the absence of that field.

Constants

Next

Voice overview

What a voice bot can and cannot do.

Publishing and receiving audio

The media session in practice, both directions.

Voice runtime

Engine setup and the symptom-to-cause table.

Client

The rest of the client surface.