#895·fonoster

[Bug] EXTERNAL voice applications are always dialed with insecure credentials, so a TLS endpoint can't be used

Author: psandersCreated Sep 12, 2026Updated Sep 12, 2026
Labelsbugvoice

Summary

GrpcClientHandler dials every EXTERNAL application endpoint with grpc.credentials.createInsecure(), so an application whose endpoint terminates TLS (behind a reverse proxy / ingress) can never be reached — and all voice session traffic to external apps travels in plaintext.

Steps to Reproduce

  1. Deploy a voice application behind a TLS-terminating reverse proxy (Envoy, nginx, Traefik) exposing gRPC over HTTP/2 on :443.
  2. Create an EXTERNAL application with endpoint: "api.example.com:443".
  3. Place a call routed to that application.

Expected Behavior

The apiserver dials the external application over TLS when the endpoint is a secure one, and the voice session is established.

Actual Behavior

The connection fails immediately. The proxy expects a TLS ClientHello and receives a plaintext HTTP/2 preface, so it drops the connection before any routing happens. Logs show voice server not available at "api.example.com:443".

Pointing the same application at a bare plaintext host:port works — which is what makes this look like a DNS/TLS problem on the deployment side when it isn't.

Relevant code — mods/apiserver/src/voice/client/GrpcClientHandler.ts:46:

typescript
this.grpcClient = new VoiceServiceClientConstructor(
  this.config.endpoint,
  grpc.credentials.createInsecure()
) as unknown as GRPCClient;

config.endpoint is app.endpoint passed through verbatim (mods/apiserver/src/voice/integrations/createCreateContainer.ts:74-79) — a bare string with no scheme. There is no secure / allowInsecure field on VoiceClientConfig (mods/common/src/voice/voice.ts:103) nor on Application in mods/common/src/protos/applications.proto, so there is no configuration path that reaches TLS. createInsecure() is the only code path.

This is inconsistent with the rest of the codebase, which already models the choice: mods/sdk/src/client/Client.ts:58 and mods/common/src/identity/getPublicKey.ts:37 both switch on allowInsecure between createInsecure() and createSsl().

Suggested Fix

Add a per-application secure flag (e.g. bool secure on Application, or accept a grpcs:// scheme in endpoint), thread it through VoiceClientConfig, and use grpc.credentials.createSsl() in GrpcClientHandler when set — mirroring the existing allowInsecure pattern. Happy to submit a PR.

Notes

Reported downstream as fonoster/qcobro#28, where it was first diagnosed. Beyond the connectivity failure, the current behavior means external voice application traffic (including the session token in gRPC metadata) is always sent unencrypted.

Priority

P2 – Normal