Appendix C: Environment Setup Recipes
Copy-paste recipes for the four most common deployment configurations. Each recipe assumes you have Python 3.11+, uv for package management, and API keys for your chosen providers.
Recipe 1: Local Development with Console Mode
Console mode connects your laptop’s microphone and speakers directly to the agent, bypassing LiveKit and telephony entirely. This is the fastest way to iterate on prompts and tool logic.
# Clone and install
git clone https://github.com/your-org/voiceclaw-agents.git
cd voiceclaw-agents/main-agent
uv sync
# Set environment variables
cp .env.example .env
# Edit .env with your API keys:
# OPENAI_API_KEY=sk-...
# DEEPGRAM_API_KEY=...
# CARTESIA_API_KEY=...
# Run in console mode
uv run python main.py console
Console mode starts the STT-LLM-TTS pipeline using your default audio devices. Speak into your microphone and the agent responds through your speakers. No LiveKit server, no SIP, no phone numbers required.
Tips:
- Use a headset to avoid echo feedback between speakers and microphone.
- Console mode does not support multiple concurrent “calls.” It is single-session only.
- All tool calls execute normally, so make sure your backend is accessible if your tools hit APIs.
Recipe 2: Self-Hosted LiveKit + SIP on Hetzner VPS
For production telephony you need a LiveKit server with SIP support. This recipe uses a single Hetzner VPS (CPX31 or higher: 4 vCPU, 8GB RAM) running LiveKit and the SIP bridge via Docker Compose.
# docker-compose.yml
services:
livekit:
image: livekit/livekit-server:latest
ports:
- '7880:7880' # HTTP
- '7881:7881' # WebSocket
- '7882:7882' # ICE/TCP
- '50000-50200:50000-50200/udp' # ICE/UDP range
volumes:
- ./livekit.yaml:/etc/livekit.yaml
command: ['--config', '/etc/livekit.yaml']
restart: unless-stopped
sip:
image: livekit/sip:latest
network_mode: host
environment:
- SIP_PORT=5060
- LIVEKIT_URL=ws://localhost:7880
- LIVEKIT_API_KEY=${LIVEKIT_API_KEY}
- LIVEKIT_API_SECRET=${LIVEKIT_API_SECRET}
restart: unless-stopped
# livekit.yaml
port: 7880
rtc:
port_range_start: 50000
port_range_end: 50200
tcp_port: 7882
use_external_ip: true
keys:
your-api-key: your-api-secret
Setup steps:
- Provision a Hetzner VPS with Ubuntu 22.04. Ensure ports 5060/UDP (SIP), 7880-7882/TCP, and 50000-50200/UDP are open in the firewall.
- Install Docker and Docker Compose.
- Copy the files above and set
LIVEKIT_API_KEYandLIVEKIT_API_SECRETin a.envfile. - Run
docker compose up -d. - Point DNS records:
livekit.yourdomain.comandsip.yourdomain.comto the VPS IP. - Configure your Telnyx SIP trunk to send calls to
sip.yourdomain.com:5060.
SSL: For WebSocket connections, place an Nginx or Caddy reverse proxy in front of LiveKit port 7880 with a TLS certificate.
Recipe 3: LiveKit Cloud
LiveKit Cloud is the managed alternative. No servers to maintain, automatic scaling, and built-in SIP support.
- Create an account at cloud.livekit.io.
- Create a project. Note your API key, API secret, and WebSocket URL (e.g.,
wss://your-project.livekit.cloud). - In the project dashboard, navigate to SIP and create an inbound trunk with your Telnyx SIP credentials.
- Create a dispatch rule that routes incoming calls to your agent.
# Set environment variables for your agent
export LIVEKIT_URL=wss://your-project.livekit.cloud
export LIVEKIT_API_KEY=APIxxxxxxx
export LIVEKIT_API_SECRET=xxxxxxxxxxxxxxxxxxxxxxx
# Deploy your agent (it connects outbound to LiveKit Cloud)
uv run python main.py start
With LiveKit Cloud, your agent process connects outbound to the cloud service. There is no inbound port to open. The agent can run anywhere: a laptop, a VPS, a container on Fly.io.
Recipe 4: CI/CD with GitHub Actions
This workflow runs linting, type checking, unit tests, and integration tests on every push and pull request. Integration tests use DeepSeek instead of OpenAI to reduce cost.
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv python install 3.11
- run: uv sync
working-directory: main-agent
- run: uv run ruff check src/
working-directory: main-agent
- run: uv run mypy src/
working-directory: main-agent
test-unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv python install 3.11
- run: uv sync
working-directory: main-agent
- run: uv run pytest tests/unit/ -q --cov=src --cov-fail-under=80
working-directory: main-agent
test-integration:
runs-on: ubuntu-latest
needs: [lint, test-unit]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv python install 3.11
- run: uv sync
working-directory: main-agent
- run: uv run pytest tests/integration/ -v
working-directory: main-agent
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
CI: true
deploy:
runs-on: ubuntu-latest
needs: [test-integration]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
working-directory: main-agent
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Key decisions in this pipeline:
- Lint and unit tests run in parallel for speed. Integration tests wait for both to pass.
- Integration tests use
DEEPSEEK_API_KEYinstead ofOPENAI_API_KEY. The test fixtures detect theCIenvironment variable and swap the LLM provider. - Deploy only on main: The deploy job only runs when changes are merged to main, not on pull requests.
- Coverage enforcement: Unit tests fail if coverage drops below 80%.
uvfor dependency management: Faster than pip, deterministic lockfile, built-in Python version management.