Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 164 additions & 36 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,68 +1,196 @@
# Compose settings for the VocaGateway.
# ============================================================================
# VocaGateway — Docker Compose settings
# ============================================================================
#
# Copy this file, then fill in a token:
#
# umask 077
# cp .env.example .env
# printf 'VOCAGATEWAY_TOKEN=%s\n' "$(openssl rand -hex 32)" >> .env
# docker compose up --detach --build
#
# Only `docker compose` reads .env. A gateway started natively (`just run`)
# takes its token from ~/.config/vocagateway/token instead, and direnv is set up
# not to export anything from here. Never commit the populated .env.
# WHO READS THIS FILE
# Only `docker compose` (and `just up` / `just down`, which call it). The
# justfile sets `dotenv-load := false`, so a natively started gateway
# (`just run`, `uv run vocagateway`) never sees anything in here — it reads
# its token from ~/.config/vocagateway/token instead.
#
# WHAT ACTUALLY REACHES THE CONTAINER
# Only the keys compose.yaml names. It lists each one explicitly, so any
# other VOCAGATEWAY_* variable put in .env is read by Compose, interpolated
# into nothing, and silently dropped. Section 7 lists the ones people most
# often expect to work here.
#
# Never commit the populated .env — .gitignore already excludes it.
#
# Sections:
# 1. Required: bearer token
# 2. Where Docker publishes the gateway
# 3. Address the pairing QR gives the phone
# 4. Which image to run
# 5. Gateway behaviour
# 6. Listener inside the container
# 7. Settings that do not belong here

# Bearer token the phone apps present on every request. At least 32 characters.
# Compose passes it to the container as a secret, not as an environment
# variable. Generate one with: openssl rand -hex 32

# ============================================================================
# 1. Required: bearer token
# ============================================================================

# Bearer token the phone apps present on every request. At least 32
# characters. Generate one with: openssl rand -hex 32
#
# Compose passes it to the container as a secret mounted at
# /run/secrets/vocagateway_token, never as an environment variable.
#
# Fill this in, or append a second VOCAGATEWAY_TOKEN= line as shown above —
# Compose uses the last assignment when a key repeats, so the appended value
# wins over this empty placeholder.
# Fill this in, or append a second VOCAGATEWAY_TOKEN= line as shown at the top
# of this file — Compose uses the last assignment when a key repeats, so the
# appended value wins over this empty placeholder.
#
# An empty value is NOT caught by `docker compose config` — that check passes
# on the placeholder below. Nothing later tells you the token is missing
# either: the gateway falls back to a secret it generates and never prints, so
# /health/live looks fine while every authenticated request and every phone
# pairing returns 401. Set it before the first `up`.
VOCAGATEWAY_TOKEN=


# ============================================================================
# 2. Where Docker publishes the gateway
# ============================================================================

# Host interface and port Docker publishes the gateway on.
# 127.0.0.1 keeps it on loopback, which is what Tailscale Serve expects. Use
# 0.0.0.0 only for a trusted LAN, with the host firewall up. Never forward this
# port from the public internet.
#
# Ignored when VOCAGATEWAY_NETWORK_MODE=host (section 3) — Compose drops the
# whole port mapping in that mode.
VOCAGATEWAY_PUBLISH_HOST=127.0.0.1
VOCAGATEWAY_PUBLISH_PORT=8765

# Uncomment on Linux Docker Engine to share the host's network namespace, so
# the pairing QR can discover the machine's real LAN address instead of the
# container's bridge IP. This ignores VOCAGATEWAY_PUBLISH_HOST and _PORT, so lock
# the port down with the host firewall first. Docker Desktop does not support
# it.
#VOCAGATEWAY_NETWORK_MODE=host

# Address the pairing QR should encode. On the default bridge network the
# container only sees its own private bridge IP (172.x), never the host's LAN
# NIC, so auto-discovery cannot produce a phone-reachable address — set this to
# the URL the phone should actually use. This is the alternative to
# VOCAGATEWAY_NETWORK_MODE=host, and the only option on Docker Desktop.
# ============================================================================
# 3. Address the pairing QR gives the phone
# ============================================================================

# On the default bridge network the container only sees its own private bridge
# IP (172.x), never the host's LAN NIC, so address auto-discovery cannot
# produce a phone-reachable address. One of the two settings in this section
# fixes that. Behind a reverse proxy, VOCAGATEWAY_PUBLIC_URL is mandatory:
# discovery cannot guess a public hostname.

# Names the address the phone should use and puts it ahead of discovery.
# Works everywhere, including Docker Desktop on macOS and Windows.
# Omit the port when TLS terminates on 443, e.g. https://gateway.example.com
#
# One caveat: this feeds the *discovered* candidate list. An address you
# previously picked and saved in the WebUI pairing card wins over it. If the
# card was ever opened before you set this, clear the old entry with **Remove**
# under "Saved addresses", or drop `pairing_url`/`pairing_urls` from
# config.json and restart.
#
# VOCAGATEWAY_PAIRING_URL is an accepted alias, checked second.
#VOCAGATEWAY_PUBLIC_URL=http://192.168.1.20:8765

# Use a prebuilt multi-architecture image instead of building the local tag.
# The alternative, on Linux Docker Engine only (Docker Desktop does not support
# it): share the host's network namespace so discovery finds the machine's real
# LAN address by itself. This discards the section 2 port mapping entirely and
# binds straight onto the host per VOCAGATEWAY_BIND_HOST/_PORT (section 6), so
# the host firewall becomes the only thing in front of the port — lock it down
# first.
#VOCAGATEWAY_NETWORK_MODE=host


# ============================================================================
# 4. Which image to run
# ============================================================================

# Tag for the `gateway` service. Setting it does not by itself switch Compose
# from building to pulling: `docker compose up --build` still builds locally
# and applies this tag to the result. To actually run a prebuilt
# multi-architecture image:
#
# docker compose pull
# docker compose up --detach --no-build
#
# The native/cuda/vulkan profile services carry fixed tags and ignore this.
#VOCAGATEWAY_IMAGE=ghcr.io/your-user/vocagateway:latest

# Serve the Swagger UI at /docs and the schema at /openapi.json. Off by
# default: the schema and UI are extra surface area, so enable this only for
# local development.
#VOCAGATEWAY_DEBUG=true

# Listener inside the container. Only meaningful under
# VOCAGATEWAY_NETWORK_MODE=host, where the container binds the host directly and
# VOCAGATEWAY_PUBLISH_HOST/_PORT are discarded; on the default bridge network
# the gateway must keep listening on all container interfaces for the published
# port to reach it.
#VOCAGATEWAY_BIND_HOST=0.0.0.0
#VOCAGATEWAY_PORT=8765
# ============================================================================
# 5. Gateway behaviour
# ============================================================================

# Pin an engine instead of letting `auto` pick the first runnable one. The
# container can run sherpa-onnx, faster-whisper, moonshine, and whisper.cpp;
# vocamac, handy, whisperkit, and mlx-audio are macOS-only and rejected here.
# Pin an engine instead of letting `auto` pick the first runnable one.
# Accepted values: auto, sherpa-onnx, faster-whisper, moonshine, whisper.cpp,
# mlx-audio, whisperkit, vocamac, handy.
#
# The container can only run the first five. The other four need macOS (and
# mlx-audio/vocamac need Apple silicon), and nothing rejects them at startup:
# the gateway comes up, the engine reports unavailable, and /health/ready stays
# 503 with no obvious cause. The `422 invalid_engine` rejection you may have
# read about applies to the WebUI picker and PUT /v1/admin/config, not to this
# variable.
#
# Setting anything other than `auto` also pins the engine process-wide and
# overrides whatever the WebUI engine picker has saved. Leave it at `auto` and
# choose in the WebUI unless you specifically want an environment-level lock.
#VOCAGATEWAY_ENGINE=auto

# How long a failed session's audio is kept so the phone can retry, and whether
# audio is deleted immediately after a successful transcription.
#VOCAGATEWAY_RETENTION_HOURS=24
#VOCAGATEWAY_DELETE_SUCCESSFUL_AUDIO=true

# Serve the Swagger UI at /docs and the schema at /openapi.json, and report the
# image's build commit in /v1/admin/status and the WebUI **Build** row. Off by
# default: the schema and UI are extra surface area, so enable this only for
# local development.
#VOCAGATEWAY_DEBUG=true


# ============================================================================
# 6. Listener inside the container
# ============================================================================

# VOCAGATEWAY_PORT is both the container's listener port and the container-side
# target of the section 2 mapping, so it is safe to change on the default
# bridge network: the host port stays VOCAGATEWAY_PUBLISH_PORT.
#
# VOCAGATEWAY_BIND_HOST is the one to leave alone on bridge — the gateway must
# keep listening on all container interfaces for the published port to reach
# it. Narrowing it is only meaningful under VOCAGATEWAY_NETWORK_MODE=host,
# where the container binds the host directly.
#VOCAGATEWAY_BIND_HOST=0.0.0.0
#VOCAGATEWAY_PORT=8765


# ============================================================================
# 7. Settings that do not belong here
# ============================================================================

# These are read from .env but never reach the container, because compose.yaml
# does not pass them. Setting them here looks like it worked and does nothing:
#
# VOCAGATEWAY_DATA_DIR, VOCAGATEWAY_MODELS_DIR, VOCAGATEWAY_CONFIG_FILE,
# VOCAGATEWAY_TOKEN_FILE
# Fixed by the image at /data, /data/models, /data/config/config.json,
# and /run/secrets/vocagateway_token. Move the data elsewhere by
# remapping the `vocagateway-data` volume, not by setting these.
#
# VOCAGATEWAY_WHISPER_BINARY, VOCAGATEWAY_WHISPER_MODEL,
# VOCAGATEWAY_WHISPERKIT_BINARY, VOCAGATEWAY_VOCAMAC_APP,
# VOCAGATEWAY_VOCAMAC_MODEL, VOCAGATEWAY_HANDY_BINARY,
# VOCAGATEWAY_HANDY_MODEL, VOCAGATEWAY_HANDY_FALLBACK_MODEL
# Native-only. The image pins its own whisper.cpp CLI, and the macOS
# engines do not exist in a Linux container.
#
# And one that must not go here even though Compose would use it:
#
# VOCAGATEWAY_GIT_COMMIT, VOCAGATEWAY_GIT_COMMIT_SUBJECT,
# VOCAGATEWAY_GIT_COMMIT_DATE
# Build args, not settings. Written into .env they would pin every later
# build to whatever commit was current when you wrote the file. `just up`
# and `just image` export them from git for you; see "Stamping the build
# commit" in the README for the direnv equivalent.
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ normalizes; a local engine transcribes; the gateway returns an idempotent
transcript. License: [AGPL-3.0](LICENSE). **Not on-device** — audio leaves the
client for the machine you run. No Voca account, no hosted Voca cloud.

Product and operator docs: [README.md](README.md),
[docs/configuration.md](docs/configuration.md),
[docs/deployment.md](docs/deployment.md), [docs/models.md](docs/models.md).
Product and operator docs: [README.md](README.md) and the
[docs index](docs/README.md) ([configuration](docs/configuration.md),
[deployment](docs/deployment.md), [tailscale](docs/tailscale.md),
[troubleshooting](docs/troubleshooting.md), [models](docs/models.md)).
This file is for coding agents.

## Critical: git worktrees for every branch and PR
Expand Down
Loading