Prerequisites
Before you begin, make sure you have the following installed:| Tool | Version | Notes |
|---|---|---|
| Go | 1.25+ | Required. Check with go version. |
| Git | Any recent | Required for cloning and branching. |
| make | Any | Optional but recommended for convenience targets. |
C compiler (gcc or clang) | Any | Required only for CGO builds (Silero VAD, Opus/WebRTC). On macOS, install Xcode Command Line Tools. On Linux, install build-essential. |
| Docker / Docker Compose | Any | Optional. Used to run local dependencies (Redis, databases) and integration tests. |
CGO is only required if you want to build with Silero VAD (
vad_type: "silero") or with WebRTC audio output (Opus encoding). For most development work, CGO_ENABLED=0 produces a smaller binary that still supports energy-based VAD and all WebSocket/telephony transports.Dev Setup
Set up a config file
Copy the example config and adjust it for your local environment:Point Voxray at the config at runtime:Key config areas to review:
transport— set to"websocket","smallwebrtc", or"both"provider,stt_provider,tts_provider— select AI provider and set API keyssession_store—"memory"(default) or"redis"withredis_urls3_*keys — for optional call recording
Run the server
- WebSocket endpoint:
ws://localhost:8080/ws - WebRTC offer endpoint:
http://localhost:8080/webrtc/offer
VOXRAY_LOG_LEVEL=debug for verbose output during development.Running Tests
Unit tests (fast, no external services)
Run the full test suite before every commit:Integration and end-to-end tests
Heavier tests live undertests/integration/ and tests/e2e/ and may require external services:
Test layout
| Directory | What lives here |
|---|---|
tests/pkg/ | Unit tests mirroring pkg/, e.g. tests/pkg/pipeline/pipeline_test.go |
tests/integration/ | Integration tests for provider combinations and transport paths |
tests/e2e/ | Full end-to-end pipeline tests |
tests/testdata/ | Shared fixtures (audio files, JSON payloads, etc.) |
- Prefer fast, deterministic unit tests under
tests/pkg/.... - For provider integrations, add coverage under
tests/pkg/services/<provider>/. - For full pipeline scenarios (WebSocket/WebRTC/telephony), add tests under
tests/e2e/ortests/integration/.
Code Style & Quality
Voxray follows standard Go best practices with a focus on correctness, observability, and safe concurrency.Formatting and linting
All Go code must be formatted before committing:CI runs a subset of these checks on every push and pull request. Fixing lint failures locally is faster than iterating through CI. Install
golangci-lint from golangci-lint.run.Context propagation
- Every request-handling entrypoint must accept a
context.Context. - Pass the context through to all downstream calls (providers, database, Redis, etc.) — never substitute
context.Background()inside a request path. - Respect context cancellation and deadlines in all I/O and long-running operations.
Error handling
- Return errors; do not
panicin normal control flow. - Wrap errors with additional context when crossing package boundaries.
- Use typed or sentinel errors where the distinction matters for callers.
- Log errors once at the pipeline boundary; avoid logging the same error at multiple layers.
Concurrency
- Avoid shared mutable state; prefer passing data through channels or pipeline frames.
- Protect shared state with mutexes and document the invariants. See
LLMProcessor.msgs(guarded bysync.Mutex) as the canonical pattern. - Tie all goroutine lifecycles to a
context.Contextso they exit cleanly on shutdown or cancellation.
Panics
Panics are reserved for truly unrecoverable programmer errors (impossible states, violated invariants). Public APIs and the frame hot path must never panic on invalid user input or transient provider errors.Conventional Commits
Voxray uses the Conventional Commits format to keep history clear and automation-friendly.Format
Common types
| Type | When to use |
|---|---|
feat | A new user-visible feature |
fix | A bug fix |
docs | Documentation changes only |
refactor | Code change that neither fixes a bug nor adds a feature |
test | Adding or updating tests |
chore | Maintenance, tooling, or non-production code |
ci | CI workflow changes |
build | Build system or dependency changes |
perf | Performance improvements |
Examples
Pull Request Process
Fork and create a branch
External contributors: fork the repository on GitHub first. All contributors: create a descriptive branch name.Use the commit type as the branch prefix (
feat/, fix/, docs/, etc.) for consistency.Make focused changes
Keep each PR logically focused — one feature or bug fix at a time. If you find unrelated issues while working, open separate PRs for them.Update or add tests alongside every code change.
Run checks locally
Before pushing, verify the full suite passes:Run any integration or e2e tests relevant to your change area if external services are available.
Update documentation
If your change affects behavior, configuration, or public API surface, update the relevant docs:
README.mdfor overview-level changes- Files under
docs/for in-depth documentation - Config examples in
configs/orconfig.example.json - Provider-specific docs if adding or changing a provider
Push and open the PR
main branch. In the PR description, include:- What changed and why
- Links to related Issues or Discussions
- Any noteworthy design decisions or trade-offs
feat(transport): add WebRTC metrics).Respond to review feedback
Push follow-up commits to address reviewer comments. Keep the branch up to date by rebasing or merging
main as needed. Respond to every comment — either implement the suggestion or explain why you disagree.What Makes a Good PR
A pull request is easier to review and more likely to be accepted when it:- Has focused scope. One logical change per PR. Large refactors are better split across multiple incremental PRs.
- Includes tests. Every new behaviour and every bug fix should have a corresponding test that would have caught the issue.
- Updates docs. If the change affects configuration keys, environment variables, API behaviour, or provider support, the documentation is updated in the same PR.
- Passes all checks.
go test ./...,go vet ./..., andgolangci-lint run ./...all pass with no new warnings. - Has a clear description. The PR description explains what changed, why, and any relevant context. Reviewers should not need to read the entire diff to understand the intent.
- Does not hardcode secrets. API keys, tokens, and credentials must always come from config or environment variables — never committed to source.
Bug Reports
High-quality bug reports make it much easier to diagnose issues in a real-time voice system. Before opening an issue, check existing Issues and Discussions to see if the problem is already known. A good bug report includes:- Summary — one or two sentences describing the problem.
- Environment — Voxray version or commit SHA, Go version, OS/architecture, deployment mode (local, Docker, Kubernetes).
- Configuration — relevant portions of your config (redact API keys and credentials).
- Steps to reproduce — a minimal, reliable sequence that reproduces the issue.
- Expected behavior — what you expected Voxray to do.
- Actual behavior — what actually happened, including error messages or incorrect output.
- Logs and metrics — relevant log excerpts (sanitized) and any Prometheus metrics that help illustrate the problem.
Links
- GitHub Repository: github.com/voxray-ai/voxray-ai
- Issues: github.com/voxray-ai/voxray-ai/issues
- Discussions: github.com/voxray-ai/voxray-ai/discussions
- Discord: See the invite link in
README.mdor the project badges.