RadioShaq is configured via a single Pydantic Settings model (radioshaq.config.schema.Config). You can use environment variables, an optional YAML/JSON file, or both. Environment variables override file values, so you can keep secrets in env and the rest in a file. You can also view or overlay LLM, memory, and per-role overrides via the web Settings page and the /api/v1/config/* endpoints (see Per-role and per-subagent overrides); use radioshaq config show to print config from file. This page explains how configuration is loaded, how to go from zero to a running station, and documents every option.
- Defaults — Every setting has a default in code (e.g.
mode: field,postgres_urlpointing to127.0.0.1:5434). - Config file — If present,
config.yaml(or the path inRADIOSHAQ_YAML_FILE/ your app’s config path) is loaded. Keys are nested (e.g.database.postgres_url,radio.enabled). JSON is also supported. - Environment variables — All top-level and nested settings can be set with the prefix
RADIOSHAQ_and double underscore__for nesting. Examples:RADIOSHAQ_MODE=fieldRADIOSHAQ_DATABASE__POSTGRES_URL=postgresql+asyncpg://user:pass@host:5432/dbRADIOSHAQ_LLM__PROVIDER=mistralRADIOSHAQ_LLM__MISTRAL_API_KEY=your-key(or setMISTRAL_API_KEY)RADIOSHAQ_RADIO__ENABLED=trueRADIOSHAQ_RADIO__RIG_MODEL=3073RADIOSHAQ_AUDIO__RESPONSE_MODE=confirm_first
Env wins over the file; the file wins over defaults. Use env for secrets and deployment-specific values, and the file for shared or readable structure.
Two reference files (synced from radioshaq/ into the docs by CI) provide exhaustive listings of every option:
- .env.example — Every environment variable:
RADIOSHAQ_*(and__for nested keys), plusDATABASE_URL/POSTGRES_*(for Alembic),MISTRAL_API_KEY,ELEVENLABS_API_KEY,RADIOSHAQ_BUS_CONSUMER_ENABLED,API_HOST,API_PORT, and other runtime variables. Copy to.envinradioshaq/and set values as needed. - config.example.yaml — Full YAML configuration mirroring the schema (core, database, jwt, llm, memory, radio, audio, field, hq, pm2). Copy to
config.yamlinradioshaq/(or set the path via your app’s config path). Env vars override file values.
Use these as the single source of truth for option names and env var spelling.
Run radioshaq setup from the radioshaq/ directory (project root) to create or update .env and config.yaml via prompts:
- First-time: Prompts for mode, database (Docker / URL / skip), JWT secret, LLM provider and API key (optional), then optional radio, memory, field/HQ, station callsign, and trigger phrases (voice activation). In radio prompts, setup now also asks whether MessageBus-driven radio replies are enabled and whether those replies should use TTS. Writes to project root by default; use
--config-dirto override. - Reconfigure:
radioshaq setup --reconfigureloads existing config and lets you change selected sections (mode, database, jwt, llm, memory, overrides). - Quick:
radioshaq setup --quickasks only for mode and “Use Docker for Postgres?” then uses defaults and can start Docker and run migrations. - CI / no-input:
radioshaq setup --no-input --mode field [--db-url ...] [--station-callsign K5ABC] [--trigger-phrases "radioshaq, field station"] [--llm-provider mistral] [--llm-model mistral-large-latest] [--custom-api-base http://localhost:11434] [--hindsight-url http://localhost:8888] [--memory-enabled] [--radio-reply-tx-enabled|--radio-reply-tx-disabled] [--radio-reply-use-tts|--radio-reply-no-tts] [--llm-overrides '{"whitelist":{"provider":"custom","model":"ollama/llama2","custom_api_base":"http://localhost:11434"}}']writes default files with no prompts; use in scripts or CI. Per-role LLM: use--llm-overrideswith a JSON object mapping role names (orchestrator,judge,whitelist,daily_summary) to partial LLM config (e.g.provider,model,custom_api_base). - Per-role LLM (interactive): In full setup or when using reconfigure and choosing overrides, you can configure per-role LLM overrides (orchestrator, judge, whitelist, daily_summary). Each override can set a different provider, model, and (for custom) API base; API keys remain in env. Use reconfigure → overrides to add or change them later.
- Config show:
radioshaq config show [--section llm|memory|overrides] [--config-dir PATH]prints LLM, memory, and per-role overrides fromconfig.yaml(API keys redacted). - Launch (dev): After setup, start dependencies and the API with
radioshaq launch docker(Postgres only),radioshaq launch docker --hindsight(Postgres + Hindsight),radioshaq launch pm2(Docker Postgres + PM2 API), orradioshaq launch pm2 --hindsight. These commands work on Windows, Linux, and macOS.
See .env.example and config.example.yaml for all options. The full design is in the Interactive Setup Plan document (in docs-archive/interactive-setup-plan.md).
To set up and operate a RadioShaq station you typically:
- Install — Clone the repo, install dependencies (e.g.
uv sync --extra dev --extra testinradioshaq/), and ensure Python 3.11+, optional Docker for Postgres, optional Node for PM2. Full automated setup: Run.\infrastructure\local\setup.ps1(Windows) or./infrastructure/local/setup.sh(Linux/macOS) fromradioshaq/to install deps, create config, start Docker Postgres (optional Hindsight), run migrations, and install PM2. - Database — Run PostgreSQL (e.g. Docker on port 5434), set
RADIOSHAQ_DATABASE__POSTGRES_URL(or use the default), run migrations (alembic upgrade headfromradioshaq/orpython radioshaq/infrastructure/local/run_alembic.py upgrade headfrom repo root). Launch CLI: Fromradioshaq/, runradioshaq launch dockerto start Postgres only, orradioshaq launch docker --hindsightto start Postgres and Hindsight; then run migrations if needed. - Auth — Set
RADIOSHAQ_JWT__SECRET_KEYto a secure value in production. Optionally adjust token expiry (RADIOSHAQ_JWT__FIELD_TOKEN_EXPIRE_HOURS, etc.). - LLM — Set provider and model (e.g.
RADIOSHAQ_LLM__PROVIDER=mistral,RADIOSHAQ_LLM__MODEL=mistral-large-latest) and the corresponding API key. For a local/custom endpoint (e.g. Ollama), setRADIOSHAQ_LLM__PROVIDER=custom,RADIOSHAQ_LLM__MODEL=ollama/llama2, andRADIOSHAQ_LLM__CUSTOM_API_BASE=http://localhost:11434. - Mode — Set
RADIOSHAQ_MODE=field(orhq,receiver) so the app knows whether it’s a field station, HQ, or receiver. - Radio (optional) — To attach a rig: set
RADIOSHAQ_RADIO__ENABLED=true,RADIOSHAQ_RADIO__RIG_MODEL(Hamlib model ID), andRADIOSHAQ_RADIO__PORT(e.g.COM3or/dev/ttyUSB0). Optionally enable FLDIGI, packet, or SDR TX and set their options. - Audio / voice (optional) — For the voice_rx pipeline (listen or respond on air): set
RADIOSHAQ_RADIO__AUDIO_INPUT_ENABLED=trueand configureaudio.*(input device, VAD, ASR, response_mode, trigger_phrases). - Run — Start the API (
uv run python -m radioshaq.api.serverorradioshaq run-apifromradioshaq/). Or useradioshaq launch pm2to start Docker Postgres (if available) and the API under PM2; add--hindsightto also run the Hindsight API (or run Hindsight in Docker withradioshaq launch docker --hindsight). Optionally enable the MessageBus consumer withRADIOSHAQ_BUS_CONSUMER_ENABLED=1. - Operate — Call
POST /messages/processwith a Bearer JWT and amessage(ortext) body; or use the web UI / voice pipeline to interact with the agent.
The rest of this page fills in every option by section.
| Option | Env var | Default | Description |
|---|---|---|---|
mode |
RADIOSHAQ_MODE |
field |
Operational mode: field, hq, or receiver. |
debug |
RADIOSHAQ_DEBUG |
false |
Enable debug behavior (e.g. verbose logging). |
log_level |
RADIOSHAQ_LOG_LEVEL |
INFO |
Log level: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
workspace_dir |
RADIOSHAQ_WORKSPACE_DIR |
~/.radioshaq |
Base workspace path (expanded). |
data_dir |
RADIOSHAQ_DATA_DIR |
~/.radioshaq/data |
Data directory (expanded). |
PostgreSQL (with optional PostGIS) is the primary store for transcripts, callsign registry, and GIS. The app uses an async URL (postgresql+asyncpg://...); Alembic uses a sync URL (replace +asyncpg with nothing or use a sync driver).
| Option | Env var | Default | Description |
|---|---|---|---|
database.postgres_url |
RADIOSHAQ_DATABASE__POSTGRES_URL |
postgresql+asyncpg://radioshaq:radioshaq@127.0.0.1:5434/radioshaq |
PostgreSQL URL; asyncpg driver is auto-added if missing. |
database.postgres_pool_size |
RADIOSHAQ_DATABASE__POSTGRES_POOL_SIZE |
10 |
Connection pool size. |
database.postgres_max_overflow |
RADIOSHAQ_DATABASE__POSTGRES_MAX_OVERFLOW |
20 |
Max overflow connections. |
database.postgres_echo |
RADIOSHAQ_DATABASE__POSTGRES_ECHO |
false |
Log SQL statements. |
database.dynamodb_table_prefix |
RADIOSHAQ_DATABASE__DYNAMODB_TABLE_PREFIX |
radioshaq |
For serverless DynamoDB. |
database.dynamodb_endpoint |
RADIOSHAQ_DATABASE__DYNAMODB_ENDPOINT |
null |
Override DynamoDB endpoint (e.g. localstack). |
database.dynamodb_region |
RADIOSHAQ_DATABASE__DYNAMODB_REGION |
us-east-1 |
AWS region for DynamoDB. |
database.redis_url |
RADIOSHAQ_DATABASE__REDIS_URL |
redis://localhost:6379/0 |
Redis URL for caching/sessions (optional). |
database.alembic_config |
RADIOSHAQ_DATABASE__ALEMBIC_CONFIG |
infrastructure/local/alembic.ini |
Alembic config path. |
database.auto_migrate |
RADIOSHAQ_DATABASE__AUTO_MIGRATE |
false |
Run migrations on startup. |
Migrations: From radioshaq/ with env set: uv run alembic upgrade head. From repo root:
# From repo root
python radioshaq/infrastructure/local/run_alembic.py upgrade headSee Quick Start for credentials and troubleshooting.
API endpoints expect a Bearer JWT. Tokens are issued by POST /auth/token (subject, role, station_id). Production must use a strong secret_key.
| Option | Env var | Default | Description |
|---|---|---|---|
jwt.secret_key |
RADIOSHAQ_JWT__SECRET_KEY |
dev-secret-change-in-production |
Signing secret; must be changed in production. |
jwt.algorithm |
RADIOSHAQ_JWT__ALGORITHM |
HS256 |
JWT algorithm. |
jwt.access_token_expire_minutes |
RADIOSHAQ_JWT__ACCESS_TOKEN_EXPIRE_MINUTES |
30 |
Access token lifetime. |
jwt.refresh_token_expire_days |
RADIOSHAQ_JWT__REFRESH_TOKEN_EXPIRE_DAYS |
7 |
Refresh token lifetime. |
jwt.field_token_expire_hours |
RADIOSHAQ_JWT__FIELD_TOKEN_EXPIRE_HOURS |
24 |
Field-station token lifetime. |
jwt.require_station_id |
RADIOSHAQ_JWT__REQUIRE_STATION_ID |
true |
Require station_id when issuing field tokens. |
The orchestrator (REACT loop), judge, whitelist agent, and daily-summary cron use an LLM. Set the provider, model, and the matching API key. For local/custom endpoints (e.g. Ollama), set provider: custom, model (e.g. ollama/llama2 or llama2), and custom_api_base (e.g. http://localhost:11434). For Hugging Face Inference Providers (serverless models from Groq, Together, etc.), set provider: huggingface, model (e.g. openai/gpt-oss-120b:groq, Qwen/Qwen2.5-7B-Instruct-1M), and huggingface_api_key or HF_TOKEN; the client uses the HF router URL as api_base. For Google Gemini (Google AI Studio), set provider: gemini, model (e.g. gemini-2.5-flash, gemini-2.5-pro), and gemini_api_key or GEMINI_API_KEY.
| Option | Env var | Default | Description |
|---|---|---|---|
llm.provider |
RADIOSHAQ_LLM__PROVIDER |
mistral |
One of: mistral, openai, anthropic, custom, huggingface, gemini. |
llm.model |
RADIOSHAQ_LLM__MODEL |
mistral-large-latest |
Model name (e.g. mistral-small-latest, gpt-4o, ollama/llama2; for huggingface: openai/gpt-oss-120b:groq, Qwen/Qwen2.5-7B-Instruct-1M; for gemini: gemini-2.5-flash, gemini-2.5-pro). |
llm.mistral_api_key |
RADIOSHAQ_LLM__MISTRAL_API_KEY |
null |
Mistral API key (or set MISTRAL_API_KEY if your code reads it). |
llm.openai_api_key |
RADIOSHAQ_LLM__OPENAI_API_KEY |
null |
OpenAI API key. |
llm.anthropic_api_key |
RADIOSHAQ_LLM__ANTHROPIC_API_KEY |
null |
Anthropic API key. |
llm.custom_api_base |
RADIOSHAQ_LLM__CUSTOM_API_BASE |
null |
Custom provider base URL (e.g. http://localhost:11434 for Ollama). Passed to LiteLLM. |
llm.custom_api_key |
RADIOSHAQ_LLM__CUSTOM_API_KEY |
null |
Custom provider API key. |
llm.gemini_api_key |
RADIOSHAQ_LLM__GEMINI_API_KEY |
null |
Gemini API key (Google AI Studio; or set GEMINI_API_KEY). |
llm.huggingface_api_key |
RADIOSHAQ_LLM__HUGGINGFACE_API_KEY |
null |
Hugging Face token for Inference Providers (or set HF_TOKEN). Token needs "Inference Providers" permission. |
llm.huggingface_api_base |
RADIOSHAQ_LLM__HUGGINGFACE_API_BASE |
null |
Optional; default https://router.huggingface.co/v1 when provider is huggingface. |
llm.temperature |
RADIOSHAQ_LLM__TEMPERATURE |
0.1 |
Sampling temperature (0–2). |
llm.max_tokens |
RADIOSHAQ_LLM__MAX_TOKENS |
4096 |
Max tokens per response. |
llm.timeout_seconds |
RADIOSHAQ_LLM__TIMEOUT_SECONDS |
60.0 |
Request timeout. |
llm.max_retries |
RADIOSHAQ_LLM__MAX_RETRIES |
3 |
Retries on failure. |
llm.retry_delay_seconds |
RADIOSHAQ_LLM__RETRY_DELAY_SECONDS |
1.0 |
Delay between retries. |
Per-callsign memory: core blocks, recent messages, daily summaries, and optional Hindsight integration for semantic recall/reflect. Embeddings run inside Hindsight; RadioShaq does not call an embedding API. Optional hindsight_embedding_model is passed to Hindsight if the service supports it.
| Option | Env var | Default | Description |
|---|---|---|---|
memory.enabled |
RADIOSHAQ_MEMORY__ENABLED |
true |
Enable memory system (recall/reflect tools, context). |
memory.hindsight_base_url |
RADIOSHAQ_MEMORY__HINDSIGHT_BASE_URL |
http://localhost:8888 |
Hindsight API base URL. |
memory.hindsight_enabled |
RADIOSHAQ_MEMORY__HINDSIGHT_ENABLED |
true |
Use Hindsight for semantic recall/reflect. |
memory.hindsight_embedding_model |
RADIOSHAQ_MEMORY__HINDSIGHT_EMBEDDING_MODEL |
null |
Optional embedding model for Hindsight (if supported). |
memory.recent_messages_limit |
RADIOSHAQ_MEMORY__RECENT_MESSAGES_LIMIT |
40 |
Max recent messages included in context. |
memory.daily_summary_days |
RADIOSHAQ_MEMORY__DAILY_SUMMARY_DAYS |
7 |
Days of daily summaries to include. |
memory.summary_timezone |
RADIOSHAQ_MEMORY__SUMMARY_TIMEZONE |
America/New_York |
Timezone for daily summary windows. |
You can override LLM and memory settings per “role” or per specialized agent. Missing fields fall back to the global llm / memory config.
| Option | Description |
|---|---|
llm_overrides |
Optional map: role or agent name → partial LLMConfig. Keys: orchestrator, judge, whitelist, daily_summary, or any agent name (e.g. whitelist, gis, radio_tx, scheduler). Only the whitelist agent uses an LLM today; other agent keys apply when/if that agent gets an LLM. |
memory_overrides |
Optional map: role → partial MemoryConfig. Keys: e.g. orchestrator, memory. |
Example (in config.yaml):
llm_overrides:
whitelist: # WhitelistAgent (per-subagent)
provider: custom
model: ollama/llama2
custom_api_base: "http://localhost:11434"
daily_summary:
model: mistral-small-latest
memory_overrides:
memory:
hindsight_base_url: "http://hindsight-alt:8888"Where to change at runtime:
- Web UI — Settings page: view/edit LLM, memory, and overrides (runtime overlay; does not persist to file).
- API —
GET/PATCH/api/v1/config/llm,/api/v1/config/memory,/api/v1/config/overrides(see API Reference); PATCH is runtime-only unless you add file persistence.
Controls the physical rig (CAT), optional FLDIGI, packet, and SDR TX. If radio.enabled is false, the rig manager is not created and radio_tx/radio_rx agents run without hardware.
| Option | Env var | Default | Description |
|---|---|---|---|
radio.enabled |
RADIOSHAQ_RADIO__ENABLED |
false |
Master switch for CAT/rig. |
radio.rig_model |
RADIOSHAQ_RADIO__RIG_MODEL |
1 |
Hamlib rig model number (e.g. 3073 = IC-7300, 127 = FT-450D). |
radio.port |
RADIOSHAQ_RADIO__PORT |
/dev/ttyUSB0 |
Serial port (e.g. COM3, /dev/ttyUSB0). |
radio.baudrate |
RADIOSHAQ_RADIO__BAUDRATE |
9600 |
Serial baud rate. |
radio.use_daemon |
RADIOSHAQ_RADIO__USE_DAEMON |
false |
Use rigctld daemon instead of direct serial. |
radio.daemon_host |
RADIOSHAQ_RADIO__DAEMON_HOST |
localhost |
rigctld host. |
radio.daemon_port |
RADIOSHAQ_RADIO__DAEMON_PORT |
4532 |
rigctld port. |
radio.fldigi_enabled |
RADIOSHAQ_RADIO__FLDIGI_ENABLED |
false |
Enable FLDIGI for digital modes. |
radio.fldigi_host |
RADIOSHAQ_RADIO__FLDIGI_HOST |
localhost |
FLDIGI server host. |
radio.fldigi_port |
RADIOSHAQ_RADIO__FLDIGI_PORT |
7362 |
FLDIGI server port. |
radio.packet_enabled |
RADIOSHAQ_RADIO__PACKET_ENABLED |
false |
Enable packet (KISS TNC). |
radio.packet_callsign |
RADIOSHAQ_RADIO__PACKET_CALLSIGN |
N0CALL |
Packet callsign. |
radio.packet_ssid |
RADIOSHAQ_RADIO__PACKET_SSID |
0 |
Packet SSID (0–15). |
radio.packet_kiss_host |
RADIOSHAQ_RADIO__PACKET_KISS_HOST |
localhost |
KISS TNC host. |
radio.packet_kiss_port |
RADIOSHAQ_RADIO__PACKET_KISS_PORT |
8001 |
KISS TNC port. |
radio.tx_enabled |
RADIOSHAQ_RADIO__TX_ENABLED |
true |
Allow transmit. |
radio.rx_enabled |
RADIOSHAQ_RADIO__RX_ENABLED |
true |
Allow receive. |
radio.max_power_watts |
RADIOSHAQ_RADIO__MAX_POWER_WATTS |
100.0 |
Max power (informational). |
radio.audio_output_device |
RADIOSHAQ_RADIO__AUDIO_OUTPUT_DEVICE |
null |
Sound device name/index feeding rig line-in (voice TX). |
radio.voice_use_tts |
RADIOSHAQ_RADIO__VOICE_USE_TTS |
false |
Use TTS when no audio file is provided. |
radio.radio_reply_tx_enabled |
RADIOSHAQ_RADIO__RADIO_REPLY_TX_ENABLED |
true |
Enable MessageBus outbound radio replies (radio_rx -> radio_tx). |
radio.radio_reply_use_tts |
RADIOSHAQ_RADIO__RADIO_REPLY_USE_TTS |
true |
For MessageBus outbound radio replies, force use_tts on/off. |
radio.tx_audit_log_path |
RADIOSHAQ_RADIO__TX_AUDIT_LOG_PATH |
null |
Path to JSONL file for TX audit log. |
radio.tx_allowed_bands_only |
RADIOSHAQ_RADIO__TX_ALLOWED_BANDS_ONLY |
true |
Restrict TX to band_plan bands. |
radio.restricted_bands_region |
RADIOSHAQ_RADIO__RESTRICTED_BANDS_REGION |
FCC |
Country/region for restricted-band enforcement: FCC, CA, CEPT, FR, UK, ES, BE, CH, LU, MC, MX, AR, CL, … (Americas), AU, ZA, NG, KE, … (Africa), NZ, JP, IN. Do not use ITU_R1 or ITU_R3 here — they are band-plan-only (no restricted bands); use band_plan_region for those. |
radio.band_plan_region |
RADIOSHAQ_RADIO__BAND_PLAN_REGION |
null |
Override band plan source (e.g. ITU_R1, ITU_R3). If null, uses the backend from restricted_bands_region. Use this for ITU region plans; keep restricted_bands_region as a country. |
radio.allowed_callsigns |
(list in YAML) | null |
Static list of allowed callsigns; merged with DB registry. |
radio.callsign_registry_required |
RADIOSHAQ_RADIO__CALLSIGN_REGISTRY_REQUIRED |
false |
If true, only registered or allowed callsigns for store/relay. |
radio.sdr_tx_enabled |
RADIOSHAQ_RADIO__SDR_TX_ENABLED |
false |
Enable HackRF (or other SDR) TX. |
radio.sdr_tx_backend |
RADIOSHAQ_RADIO__SDR_TX_BACKEND |
hackrf |
SDR backend name. |
radio.sdr_tx_device_index |
RADIOSHAQ_RADIO__SDR_TX_DEVICE_INDEX |
0 |
HackRF device index. |
radio.sdr_tx_serial |
RADIOSHAQ_RADIO__SDR_TX_SERIAL |
null |
HackRF serial (optional). |
radio.sdr_tx_max_gain |
RADIOSHAQ_RADIO__SDR_TX_MAX_GAIN |
47 |
Max TX gain (0–47). |
radio.sdr_tx_allow_bands_only |
RADIOSHAQ_RADIO__SDR_TX_ALLOW_BANDS_ONLY |
true |
Restrict SDR TX to allowed bands. |
radio.audio_input_enabled |
RADIOSHAQ_RADIO__AUDIO_INPUT_ENABLED |
false |
Enable voice_rx pipeline (capture from rig). |
radio.audio_output_enabled |
RADIOSHAQ_RADIO__AUDIO_OUTPUT_ENABLED |
false |
Enable audio output to rig. |
radio.audio_monitoring_enabled |
RADIOSHAQ_RADIO__AUDIO_MONITORING_ENABLED |
false |
Enable monitoring path. |
radio.voice_listener_enabled |
RADIOSHAQ_RADIO__VOICE_LISTENER_ENABLED |
true |
When true and audio_input_enabled, run voice listener so rig audio is captured, transcribed, and published to the message queue (default capture path for orchestrator/relay). |
radio.voice_listener_cycle_seconds |
RADIOSHAQ_RADIO__VOICE_LISTENER_CYCLE_SECONDS |
3600.0 |
Duration (seconds) per voice monitor cycle; clamped 60–86400. |
radio.voice_store_transcript |
RADIOSHAQ_RADIO__VOICE_STORE_TRANSCRIPT |
false |
When true, store each voice segment as a transcript (metadata band, source=voice_listener) for GET /transcripts and relay. |
radio.default_band |
RADIOSHAQ_RADIO__DEFAULT_BAND |
null |
Default band when listen_bands is not set (e.g. 40m, 2m). |
radio.listen_bands |
(list in YAML) | null |
Bands to monitor (e.g. [40m, 2m]). If null, only default_band is used when set. |
radio.listener_enabled |
RADIOSHAQ_RADIO__LISTENER_ENABLED |
false |
Run the background band listener when bands are configured. |
radio.listener_cycle_seconds |
RADIOSHAQ_RADIO__LISTENER_CYCLE_SECONDS |
30.0 |
Seconds per band in round-robin mode (single receiver). Clamped 5–300. |
radio.listener_concurrent_bands |
RADIOSHAQ_RADIO__LISTENER_CONCURRENT_BANDS |
true |
If true, one monitor task per band in parallel; if false, single receiver round-robin. |
radio.relay_inject_target_band |
RADIOSHAQ_RADIO__RELAY_INJECT_TARGET_BAND |
false |
When relaying (no deliver_at), inject the relayed message into the target band RX queue. |
radio.relay_tx_target_band |
RADIOSHAQ_RADIO__RELAY_TX_TARGET_BAND |
false |
When relaying (no deliver_at), transmit the relayed message on the target band via radio_tx. |
Relay: Relay is store-only by default. Recipients get messages by polling GET /transcripts?callsign=<their_callsign>&destination_only=true&band=<target_band>. When relay_inject_target_band or relay_tx_target_band is enabled, they apply to both the API and the orchestrator relay tool.
Compliance and region support: TX is checked against restricted bands and (when tx_allowed_bands_only is true) the effective band plan. The compliance plugin provides region-specific backends:
| Backend key | Restricted bands | Band plan | Typical use |
|---|---|---|---|
FCC |
US 47 CFR §15.205 | Default (ITU R2) | United States |
CA |
FCC baseline (ISED/RBR-4) | Default (ITU R2) | Canada |
CEPT |
EU harmonised (ERC 70-03, ETSI) | IARU R1 (2m 144–146 MHz, 70cm 430–440 MHz) | EU general |
FR |
Same as CEPT | Same as CEPT | France |
UK |
Same as CEPT | Same as CEPT | United Kingdom |
ES |
Same as CEPT | Same as CEPT | Spain |
BE |
Same as CEPT | Same as CEPT | Belgium |
CH |
Same as CEPT | Same as CEPT | Switzerland |
LU |
Same as CEPT | Same as CEPT | Luxembourg |
MC |
Same as CEPT | Same as CEPT | Monaco |
ITU_R1 |
None (band-plan only) | IARU R1 | Override band plan only |
ITU_R3 |
None (band-plan only) | IARU R3 (2m 144–148, 70cm 430–440 MHz) | Override band plan for Asia–Pacific |
MX |
FCC baseline (IFT may vary) | Default (ITU R2) | Mexico |
AR, CL, CO, PE, VE, EC, UY, PY, BO, CR, PA, GT, DO |
FCC baseline | Default (ITU R2) | Argentina, Chile, Colombia, Peru, Venezuela, Ecuador, Uruguay, Paraguay, Bolivia, Costa Rica, Panama, Guatemala, Dominican Republic |
AU |
Enforced (ACMA conservative) | IARU R3 | Australia |
ZA |
Enforced (ICASA NRFP) | IARU R1 | South Africa |
NG, KE, EG, MA, … (see Response & compliance) |
Enforced (R1 conservative) | IARU R1 | Nigeria, Kenya, Egypt, Morocco, etc. |
NZ |
Enforced (RSM PIB 21 conservative) | IARU R3 | New Zealand |
JP |
Enforced (conservative set) | IARU R3 | Japan |
IN |
Enforced (conservative set) | IARU R3 | India |
Set restricted_bands_region: CEPT (or FR, UK, ES, BE, CH, LU, MC) for EU/EEA to enforce CEPT-style restricted bands and R1 band edges. For Americas use CA, MX, or country code (AR, CL, etc.). For Australia/Asia–Pacific use AU or ITU_R3. For Africa use country code (ZA, NG, KE, etc.) — R1 band plan, national rules apply. Use band_plan_region: ITU_R1 or ITU_R3 to override band plan. See Response & compliance for official sources and country→backend mapping. Operators must verify national rules (e.g. ANFR, Ofcom, ACMA, IFT, ISED, ICASA, NCC).
When radio.voice_use_tts is true or a task sets use_tts: true, speech is generated from text using the configured TTS provider. Options live under tts.*.
| Option | Env var | Default | Description |
|---|---|---|---|
tts.provider |
RADIOSHAQ_TTS__PROVIDER |
elevenlabs |
elevenlabs (API; set ELEVENLABS_API_KEY) or kokoro (local; run uv sync --extra tts_kokoro). |
tts.elevenlabs_voice_id |
RADIOSHAQ_TTS__ELEVENLABS_VOICE_ID |
(Rachel) | ElevenLabs voice ID. |
tts.elevenlabs_model_id |
RADIOSHAQ_TTS__ELEVENLABS_MODEL_ID |
eleven_multilingual_v2 |
ElevenLabs model (e.g. eleven_turbo_v2_5, eleven_flash_v2_5). |
tts.elevenlabs_output_format |
RADIOSHAQ_TTS__ELEVENLABS_OUTPUT_FORMAT |
mp3_44100_128 |
Output format. |
tts.kokoro_voice |
RADIOSHAQ_TTS__KOKORO_VOICE |
af_heart |
Kokoro voice name (e.g. am_michael, bf_emma). |
tts.kokoro_lang_code |
RADIOSHAQ_TTS__KOKORO_LANG_CODE |
a |
Language code: a (US English), b (UK English), e, f, etc. |
tts.kokoro_speed |
RADIOSHAQ_TTS__KOKORO_SPEED |
1.0 |
Speech rate (0.5–2.0). |
Kokoro (local TTS): Install with uv sync --extra tts_kokoro. This pulls in kokoro, soundfile, and Kokoro’s own dependencies (e.g. torch, transformers, misaki[en]). On Linux, the soundfile package may require the system library libsndfile (e.g. apt install libsndfile1 or dnf install libsndfile).
When radio.audio_input_enabled is true, the voice_rx pipeline captures audio from the rig, runs VAD and ASR, applies trigger/phrase filtering, and optionally queues a response for human approval or auto-responds. These settings live under audio.*.
| Option | Env var | Default | Description |
|---|---|---|---|
audio.input_device |
RADIOSHAQ_AUDIO__INPUT_DEVICE |
null |
Audio input device (rig line-out). |
audio.input_sample_rate |
RADIOSHAQ_AUDIO__INPUT_SAMPLE_RATE |
16000 |
Sample rate (Hz). |
audio.output_device |
RADIOSHAQ_AUDIO__OUTPUT_DEVICE |
null |
Audio output device (e.g. rig line-in). |
audio.preprocessing_enabled |
RADIOSHAQ_AUDIO__PREPROCESSING_ENABLED |
true |
Enable preprocessing. |
audio.agc_enabled |
RADIOSHAQ_AUDIO__AGC_ENABLED |
true |
Automatic gain control. |
audio.agc_target_rms |
RADIOSHAQ_AUDIO__AGC_TARGET_RMS |
0.1 |
AGC target RMS. |
audio.highpass_filter_enabled |
RADIOSHAQ_AUDIO__HIGHPASS_FILTER_ENABLED |
true |
High-pass filter. |
audio.highpass_cutoff_hz |
RADIOSHAQ_AUDIO__HIGHPASS_CUTOFF_HZ |
80.0 |
High-pass cutoff (Hz). |
audio.denoising_enabled |
RADIOSHAQ_AUDIO__DENOISING_ENABLED |
true |
Denoising. |
audio.denoising_backend |
RADIOSHAQ_AUDIO__DENOISING_BACKEND |
rnnoise |
rnnoise, spectral, or none. |
audio.noise_calibration_seconds |
RADIOSHAQ_AUDIO__NOISE_CALIBRATION_SECONDS |
3.0 |
Noise calibration duration. |
audio.min_snr_db |
RADIOSHAQ_AUDIO__MIN_SNR_DB |
3.0 |
Minimum SNR (dB). |
audio.vad_enabled |
RADIOSHAQ_AUDIO__VAD_ENABLED |
true |
Voice activity detection. |
audio.vad_threshold |
RADIOSHAQ_AUDIO__VAD_THRESHOLD |
0.02 |
VAD threshold. |
audio.vad_mode |
RADIOSHAQ_AUDIO__VAD_MODE |
aggressive |
normal, low, aggressive, very_aggressive. |
audio.pre_speech_buffer_ms |
RADIOSHAQ_AUDIO__PRE_SPEECH_BUFFER_MS |
300 |
Ms of audio before speech start. |
audio.post_speech_buffer_ms |
RADIOSHAQ_AUDIO__POST_SPEECH_BUFFER_MS |
400 |
Ms of audio after speech end. |
audio.min_speech_duration_ms |
RADIOSHAQ_AUDIO__MIN_SPEECH_DURATION_MS |
500 |
Min segment length. |
audio.max_speech_duration_ms |
RADIOSHAQ_AUDIO__MAX_SPEECH_DURATION_MS |
30000 |
Max segment length. |
audio.silence_duration_ms |
RADIOSHAQ_AUDIO__SILENCE_DURATION_MS |
800 |
Silence to end segment. |
audio.asr_model |
RADIOSHAQ_AUDIO__ASR_MODEL |
voxtral |
ASR backend: voxtral, whisper (local; install with uv sync --extra audio), or scribe (ElevenLabs API; set ELEVENLABS_API_KEY). |
audio.asr_language |
RADIOSHAQ_AUDIO__ASR_LANGUAGE |
en |
ASR language (en, fr, es, or auto). |
audio.asr_min_confidence |
RADIOSHAQ_AUDIO__ASR_MIN_CONFIDENCE |
0.6 |
Min ASR confidence (0–1). |
audio.response_mode |
RADIOSHAQ_AUDIO__RESPONSE_MODE |
listen_only |
listen_only, confirm_first, auto_respond, confirm_timeout. |
audio.response_timeout_seconds |
RADIOSHAQ_AUDIO__RESPONSE_TIMEOUT_SECONDS |
30.0 |
Timeout for confirm_timeout mode. |
audio.response_delay_ms |
RADIOSHAQ_AUDIO__RESPONSE_DELAY_MS |
500 |
Delay before sending response. |
audio.response_cooldown_seconds |
RADIOSHAQ_AUDIO__RESPONSE_COOLDOWN_SECONDS |
5.0 |
Cooldown between responses. |
audio.trigger_enabled |
RADIOSHAQ_AUDIO__TRIGGER_ENABLED |
true |
Only process when trigger phrase detected. |
audio.trigger_phrases |
(list in YAML) | ["radioshaq", "field station"] |
Phrases that trigger processing. |
audio.trigger_match_mode |
RADIOSHAQ_AUDIO__TRIGGER_MATCH_MODE |
contains |
exact, contains, starts_with, fuzzy. |
audio.trigger_callsign |
RADIOSHAQ_AUDIO__TRIGGER_CALLSIGN |
null |
Optional callsign filter. |
audio.trigger_min_confidence |
RADIOSHAQ_AUDIO__TRIGGER_MIN_CONFIDENCE |
0.7 |
Min confidence for trigger match. |
audio.audio_activation_enabled |
RADIOSHAQ_AUDIO__AUDIO_ACTIVATION_ENABLED |
false |
Require activation phrase before processing. |
audio.audio_activation_phrase |
RADIOSHAQ_AUDIO__AUDIO_ACTIVATION_PHRASE |
radioshaq |
Phrase to activate session. |
audio.audio_activation_mode |
RADIOSHAQ_AUDIO__AUDIO_ACTIVATION_MODE |
session |
session (once) or per_message. |
audio.ptt_coordination_enabled |
RADIOSHAQ_AUDIO__PTT_COORDINATION_ENABLED |
true |
PTT coordination for half-duplex. |
audio.ptt_cooldown_ms |
RADIOSHAQ_AUDIO__PTT_COOLDOWN_MS |
500 |
PTT cooldown (ms). |
audio.break_in_enabled |
RADIOSHAQ_AUDIO__BREAK_IN_ENABLED |
true |
Allow break-in. |
Response modes:
- listen_only — Transcribe only; no TX.
- confirm_first — Queue proposed response for human approval.
- auto_respond — Send response automatically (use with caution).
- confirm_timeout — Auto-send after a timeout if not rejected.
When mode: field, these options apply (HQ connection and sync).
| Option | Env var | Default | Description |
|---|---|---|---|
field.station_id |
RADIOSHAQ_FIELD__STATION_ID |
FIELD-01 |
Unique field station ID. |
field.callsign |
RADIOSHAQ_FIELD__CALLSIGN |
null |
Optional callsign. |
field.hq_base_url |
RADIOSHAQ_FIELD__HQ_BASE_URL |
https://hq.radioshaq.example.com |
HQ API base URL. |
field.hq_ws_url |
RADIOSHAQ_FIELD__HQ_WS_URL |
(derived) | WebSocket URL (default from base). |
field.hq_auth_token |
RADIOSHAQ_FIELD__HQ_AUTH_TOKEN |
null |
Token for HQ API. |
field.sync_interval_seconds |
RADIOSHAQ_FIELD__SYNC_INTERVAL_SECONDS |
60 |
Sync interval to HQ. |
field.sync_batch_size |
RADIOSHAQ_FIELD__SYNC_BATCH_SIZE |
100 |
Batch size for sync. |
field.sync_max_retries |
RADIOSHAQ_FIELD__SYNC_MAX_RETRIES |
5 |
Max sync retries. |
field.sync_retry_delay |
RADIOSHAQ_FIELD__SYNC_RETRY_DELAY |
10 |
Delay between retries (s). |
field.sync_on_connect |
RADIOSHAQ_FIELD__SYNC_ON_CONNECT |
true |
Sync on connect. |
field.offline_mode |
RADIOSHAQ_FIELD__OFFLINE_MODE |
false |
Disable HQ sync. |
field.max_offline_queue_size |
RADIOSHAQ_FIELD__MAX_OFFLINE_QUEUE_SIZE |
1000 |
Max queued items when offline. |
When mode: hq, these options apply.
| Option | Env var | Default | Description |
|---|---|---|---|
hq.host |
RADIOSHAQ_HQ__HOST |
0.0.0.0 |
Bind host. |
hq.port |
RADIOSHAQ_HQ__PORT |
8000 |
Bind port (overridden by API_HOST/API_PORT or RADIOSHAQ_API_HOST/RADIOSHAQ_API_PORT at runtime). |
hq.ws_enabled |
RADIOSHAQ_HQ__WS_ENABLED |
true |
Enable WebSocket. |
hq.ws_path |
RADIOSHAQ_HQ__WS_PATH |
/ws |
WebSocket path. |
hq.max_field_stations |
RADIOSHAQ_HQ__MAX_FIELD_STATIONS |
100 |
Max field stations. |
hq.field_auth_required |
RADIOSHAQ_HQ__FIELD_AUTH_REQUIRED |
true |
Require auth for field. |
hq.field_registration_open |
RADIOSHAQ_HQ__FIELD_REGISTRATION_OPEN |
false |
Allow field registration. |
hq.auto_coordination_enabled |
RADIOSHAQ_HQ__AUTO_COORDINATION_ENABLED |
true |
Auto coordination. |
hq.coordination_interval_seconds |
RADIOSHAQ_HQ__COORDINATION_INTERVAL_SECONDS |
30 |
Coordination interval. |
RadioShaq can send and receive SMS and WhatsApp messages via Twilio (same account for both). Outbound delivery is handled by the single outbound dispatcher when the MessageBus consumer is enabled.
| Option | Env var | Default | Description |
|---|---|---|---|
twilio.account_sid |
RADIOSHAQ_TWILIO__ACCOUNT_SID |
null |
Twilio Account SID (required for SMS and WhatsApp send). |
twilio.auth_token |
RADIOSHAQ_TWILIO__AUTH_TOKEN |
null |
Twilio Auth Token. |
twilio.from_number |
RADIOSHAQ_TWILIO__FROM_NUMBER |
null |
SMS sender phone number (E.164, e.g. +15551234567). |
twilio.whatsapp_from |
RADIOSHAQ_TWILIO__WHATSAPP_FROM |
null |
WhatsApp sender number (E.164); must be WhatsApp-enabled in Twilio. Optional; if unset, the WhatsApp agent is registered but returns "not configured" on send. |
All use the RADIOSHAQ_TWILIO__ prefix. See reference/.env.example for a commented template.
Config file (YAML): You can set the same under twilio in config.yaml:
twilio:
account_sid: "ACxxxx"
auth_token: "your-auth-token"
from_number: "+15551234567"
whatsapp_from: "+15551234567" # optional; same or different number enabled for WhatsAppEnvironment variables override file values.
Behavior:
- SMS: If
account_sid,auth_token, andfrom_numberare set, the SMS agent sends via Twilio. Otherwise, send returnssuccess: falsewithreason: "twilio_not_configured". - WhatsApp: If
whatsapp_fromis also set (and Twilio client exists), the WhatsApp agent sends via Twilio WhatsApp Business API (whatsapp:+E.164). Otherwise, the agent is still registered but returns "Twilio WhatsApp not configured" on send. - Inbound: Configure Twilio webhooks (SMS and/or WhatsApp) to POST to your Lambda or directly to
https://<your-hq>/internal/bus/inboundwith a body like{"channel": "sms"|"whatsapp", "chat_id": "<sender_phone>", "sender_id": "...", "content": "..."}. See Twilio WhatsApp webhooks and opt-in requirements.
Notify when a message is left for you (§8.1, §8.3): Whitelisted callsigns can opt in to receive a short SMS or WhatsApp notification when a message is delivered to them on radio (notify-on-relay). Set contact preferences via GET/PATCH /callsigns/registered/{callsign}/contact-preferences; in strict regions (e.g. EU/UK/ZA), consent_confirmed: true is required when enabling. Recipients can reply STOP to opt out; configure your Twilio webhook (or Lambda) to call POST /internal/opt-out with {"phone": "+1234567890", "channel": "sms"} or {"callsign": "K5ABC", "channel": "whatsapp"}. See Response & compliance and the project doc Notify and emergency compliance plan (in radioshaq/docs/) for region-specific consent and opt-out rules.
References: Twilio WhatsApp API overview, Twilio WhatsApp quickstart (Python), WhatsApp opt-in requirements (required for production).
Used when running under PM2 (e.g. ecosystem.config.js). Log and process settings.
| Option | Env var | Default | Description |
|---|---|---|---|
pm2.instances |
RADIOSHAQ_PM2__INSTANCES |
1 |
Number of instances. |
pm2.autorestart |
RADIOSHAQ_PM2__AUTORESTART |
true |
Auto restart. |
pm2.watch |
RADIOSHAQ_PM2__WATCH |
true |
Watch files. |
pm2.max_memory_restart |
RADIOSHAQ_PM2__MAX_MEMORY_RESTART |
1G |
Restart if memory exceeds. |
pm2.log_dir |
RADIOSHAQ_PM2__LOG_DIR |
logs |
Log directory. |
pm2.log_date_format |
RADIOSHAQ_PM2__LOG_DATE_FORMAT |
YYYY-MM-DD HH:mm:ss Z |
Log date format. |
pm2.merge_logs |
RADIOSHAQ_PM2__MERGE_LOGS |
false |
Merge logs. |
pm2.env_file |
RADIOSHAQ_PM2__ENV_FILE |
.env |
Env file. |
pm2.source_map_support |
RADIOSHAQ_PM2__SOURCE_MAP_SUPPORT |
true |
Enable source map support. |
- MessageBus consumer — The API can run an inbound message consumer in the background so external systems can push work into the REACT loop. Set
RADIOSHAQ_BUS_CONSUMER_ENABLED=1(ortrue/yes) to enable it. If not set, the consumer is disabled. - API host/port — The server uses
API_HOST/API_PORTorRADIOSHAQ_API_HOST/RADIOSHAQ_API_PORTwhen starting uvicorn (default fromhq.hostandhq.port). - CLI — Scripts that call the API use
RADIOSHAQ_API(base URL, defaulthttp://localhost:8000) andRADIOSHAQ_TOKEN(Bearer token). - TTS — When
radio.voice_use_ttsis true (or when a task setsuse_tts: true), speech is generated pertts.provider:elevenlabs(setELEVENLABS_API_KEY) orkokoro(local;uv sync --extra tts_kokoro). See TTS (text-to-speech). - ASR — Voice pipeline and audio upload use
audio.asr_model:voxtral/whisper(local;uv sync --extra audio) orscribe(ElevenLabs API;ELEVENLABS_API_KEY). - Alembic — Migrations read
DATABASE_URLorPOSTGRES_HOST,POSTGRES_PORT,POSTGRES_DB,POSTGRES_USER,POSTGRES_PASSWORD(see .env.example).
For a minimal path from zero to a running station, follow Quick Start; for hardware and rig-specific details, see Radio Usage.