pipedog is an unofficial community port of Pipecat, not affiliated with Daily.
pipedog

Node, Bun and Deno servers

Run a pipedog voice agent as a long-lived server process, with SmallWebRTC or WebSocket sessions.

A server process is the most flexible host: it runs both transports, local turn detection models, and any framework. The voice bot guide builds one with SmallWebRTC, and Frameworks mounts the same endpoints in SvelteKit, Hono, Next.js and others.

The process

Each session runs in the same process, as its own pipeline and PipelineWorker. Start one per connection, from the endpoint handler, and end it when the client disconnects:

transport.eventHandler("on_client_disconnected", async () => {
  await runner.cancel();
});

The shared agent in examples/framework-snippets/shared/agent.ts does exactly this. Pass handleSigint: false and handleSigterm: false to each session's WorkerRunner, so one session doesn't take the process's signals; handle shutdown in the server.

Runtimes

  • Bun runs the agent and upgrades WebSockets in Bun.serve: pass the server Bun gives fetch to websocketFetchHandler's handler and serve with bunWebSocket. See Bun.
  • Node.js 22 or later. Use a framework's Node adapter (Express, Hono's @hono/node-server, SvelteKit's adapter-node), or nodeRequestListener from @pipedog/transport-smallwebrtc/http for a plain node:http server. For WebSocket endpoints, serveFetch() from @pipedog/transport-websocket/serve serves a fetch handler with upgrades (with the ws package).
  • Deno serves both endpoints with Deno.serve. See Deno.

serveFetch(handler, { port }) listens with the same fetch handler on all three runtimes, so one server.ts runs on Bun, Node and Deno; examples/twilio-phone-bot is served this way.

The development runner

@pipedog/runner is Pipecat's development runner. main() serves your bot() with the prebuilt browser client, SmallWebRTC signalling and the telephony webhooks and WebSockets, from flags (bun bot.ts -t twilio --proxy ...). For a server you deploy, build the same app with createRunnerApp() and listen with serve(), or mount its fetch in your own server:

import { createRunnerApp } from "@pipedog/runner/run";
import { serve } from "@pipedog/runner/serve";

const runner = await createRunnerApp({ bot, transport: "twilio", proxy: "bot.example.com" });
await serve(runner, { host: "0.0.0.0", port: 7860 });

serve() runs on Bun, Node (with ws) and Deno. The runner's routes don't authenticate, as upstream's don't; put your checks in front of them. See Mounting the runner.

SmallWebRTC in production

SmallWebRTC is a peer-to-peer WebRTC connection between the browser and your server. The media runs over UDP, so:

  • the server needs UDP reachable from clients, on the ports the WebRTC stack picks;
  • behind NAT, give the transport ICE servers (STUN, and TURN for restrictive networks);
  • one server holds every session's media, so scale by adding servers and routing sessions to them.

See SmallWebRTC for the transport's options.

WebSocket in production

WebSocket sessions need only TCP, so they pass through ordinary load balancers and proxies. Keep idle timeouts on the proxy longer than a call, or send keepalives. Each socket stays on the server that accepted it for its lifetime. See WebSocket.

Containers

A container image needs the runtime and your app. If you use local turn detection (@pipedog/audio-silero, @pipedog/audio-smart-turn), the ONNX models load at startup from a path or URL you give them; bake them into the image or download them on start. See Turn detection.

Sizing

Each session decodes, resamples and encodes audio, and keeps provider connections open. Measure CPU and memory per session under real load, and cap concurrent sessions per process. Provider rate limits often bind before CPU does.

pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.

Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).

On this page