# ═══════════════════════════════════════════════════════════════
# openvibe.bot — Environment Configuration
# Copy to .env for development. In production this lives at /etc/openvibe/bot.env (0600).
# Names only here; never commit a value.
# ═══════════════════════════════════════════════════════════════
# Server
PORT=4630
HOST=127.0.0.1
NODE_ENV=development
BASE_URL=http://localhost:4630
TRUST_PROXY=1
# ── Data: PostgreSQL + Valkey (ADR-035) ──────────────────────
# In production OpenVibe.Host writes all four (roles/data/add-service.sh bot). Unset in development:
# an embedded PGlite database in data/pglite (one process). Tests need neither.
DATABASE_URL=
DATABASE_DIRECT_URL=
VALKEY_URL=
VALKEY_PREFIX=ov:bot:
# ── Identity: OpenVibe.Network ──────────────────────────────
OV_NETWORK_URL=https://openvibe.network
OV_NETWORK_INTERNAL_URL=http://127.0.0.1:4000
#OV_NETWORK_ISSUER=https://openvibe.network
# Optional: pin the Network's RS256 public key (PEM, \n escaped) instead of fetching the JWKS.
#OV_NETWORK_PUBLIC_KEY=
# Audience Bot accepts on incoming service tokens.
BOT_AUDIENCE=openvibe.bot
OV_OAUTH_CLIENT_ID=bot
OV_OAUTH_CLIENT_SECRET=
#OV_OAUTH_REDIRECT_URI=https://openvibe.bot/auth/callback
#COOKIE_SECURE=true
# ── OpenVibe.Events ─────────────────────────────────────────
# Relay of Bot's outbox (bot.*); off when unset (rows wait in bot_event_outbox).
#EVENTS_URL=http://127.0.0.1:4300
# OpenVibe.Events → Bot (ADR-033 account export and deletion): signs a delivery to POST /internal/events
# (comma-separated for rotation, 32+ characters each). Unset: the route answers 503 and no subscription is created.
#BOT_EVENTS_SECRET=
#EVENTS_RELAY_INTERVAL_MS=2000
# ── Pairing and device link (ADR-043) ───────────────────────
# Who mints pairing codes: bot (the default) or network. Any other value refuses to boot.
#BOT_PAIRING_AUTHORITY=bot
#BOT_PAIRING_TTL_MS=600000
#BOT_PAIRING_MAX_TRIES=5
#BOT_HEARTBEAT_MS=1000
# A person's input on a machine's screen holds it for them this long; an agent's input there is refused meanwhile (ms).
#BOT_PERSON_TURN_MS=15000
# Vibe by voice (server/voice.js): OpenVibe.Chat's loopback origin (empty turns voice off) and questions a machine an hour.
#BOT_CHAT_INTERNAL_URL=http://127.0.0.1:4400
#BOT_VOICE_PER_HOUR=30
#BOT_OFFLINE_MISSES=2
#BOT_OFFLINE_GRACE_MS=3000
#BOT_ROTATE_GRACE_MS=60000
#BOT_NODE_REAUTH_MS=330000
# ── Control ─────────────────────────────────────────────────
#BOT_MAX_COMMAND_MS=300
#BOT_HOLD_RESEND_MS=150
#BOT_TELEMETRY_HZ=2
#BOT_QUEUE_TURN_MS=60000
#BOT_QUEUE_TURN_BUDGET=50
#BOT_CONTROL_COOLDOWN_MS=0
# ── Limits / jobs / installer ───────────────────────────────
#BOT_LIMITS_MINUTE=120
#BOT_LIMITS_HOUR=3000
#BOT_JOBS=on
#BOT_JOBS_INTERVAL_MS=5000
#BOT_INSTALLER_URL=https://openvibe.link/install
# Where GET /install redirects (302): OpenVibe Link's installer script. Defaults to the checksummed asset on
# OpenVibe Link's latest release (install.sh, pinned there by its CI cmp guard so it never drifts with main).
# https on raw.githubusercontent.com, github.com, objects.githubusercontent.com or openvibe.bot only; anything
# else refuses to boot.
#BOT_INSTALLER_SOURCE_URL=https://github.com/OpenVibers/OpenVibe.Link/releases/latest/download/install.sh
# The WHIP ingest base each device publishes to; whip_url = /. Unset or empty: pair without video.
# In production this is OpenRestream's WHIP ingest, https://ingest.openre.stream/whip (O30); the key must be one
# OpenRestream issued (BOT_OPENRE_* below), or the ingest refuses the device.
#BOT_WHIP_BASE=https://example.invalid/whip
# OpenRestream, which issues each robot's publish key (its stream's ingest key). Bot mints its own Network
# service token (audience openvibe.openre) from OV_OAUTH_CLIENT_ID/SECRET and OV_NETWORK_INTERNAL_URL, holding
# openre.stream.read, openre.stream.write, openre.key.rotate and openre.session.read/openre.output.read/
# openre.output.write (session and outputs, for panel video and restreaming). BOT_OPENRE_TOKEN overrides it
# with an operator-minted service token of the same grant. BOT_OPENRE_URL unset: devices pair without video.
#BOT_OPENRE_URL=
#BOT_OPENRE_TOKEN=
#BOT_OPENRE_TIMEOUT_MS=8000
# ── Server-side ONVIF cameras (camera.onvif) ────────────────
# A JSON object keyed by robot id (or "*" for any ONVIF-profile robot) → its camera's ONVIF PTZ service.
# Bot connects server-side, writes a kind server device row and translates ptz commands to ONVIF; the profile
# carries no host or credential. username_ref/password_ref name the environment variables (or secret-store
# keys) holding the ONVIF user and password; the values are read per request and never stored or logged. The
# URL must be http(s), on one host, with no embedded credentials, or Bot refuses to boot.
#BOT_ONVIF_CAMERAS={"rob_01J8Z4M2Q0R7T9YV3K6N8P1W2X":{"url":"http://192.168.1.50/onvif/ptz_service","username_ref":"CAM_USER","password_ref":"CAM_PASSWORD"}}
#BOT_ONVIF_TIMEOUT_MS=5000
# ── OpenVibe.Billing: job usage (plan T14 L1) ───────────────
# Where the per-second readings of jobs run on paired Links go: billing.usage.record, POST /api/v1/usage.
# Bot mints its own Network service token (audience openvibe.billing, capability billing.usage.record) from
# OV_OAUTH_CLIENT_ID/SECRET and OV_NETWORK_INTERNAL_URL; BOT_BILLING_TOKEN, an operator-minted service token of
# the same grant, overrides it. BOT_BILLING_URL unset: readings wait in run_usage_outbox (never dropped) and a
# later relay delivers each once.
#BOT_BILLING_URL=http://127.0.0.1:4600
#BOT_BILLING_TOKEN=
#BOT_BILLING_INTERVAL_MS=2000
#BOT_BILLING_TIMEOUT_MS=5000
# ── OpenVibe.AI: the models on people's machines (ai-metrics.md) ─
# Every `ai` command's answer is one ai.call-report@1 to AI's POST /api/v1/usage/calls, so the local models on
# a person's machine are in the same meter as every other AI call. Bot mints its own Network service token (audience
# openvibe.ai, capability ai.usage.report) from OV_OAUTH_CLIENT_ID/SECRET and OV_NETWORK_INTERNAL_URL. Unset (or no
# client secret): metering is off and an ai command's answer is unchanged.
#OV_AI_INTERNAL_URL=http://127.0.0.1:4700
# ── Embeddable panel (plan T15 R9) ──────────────────────────
# Pages that may frame a robot's read-only panel (CSP frame-ancestors), comma or space separated. Each entry is a
# bare https origin (no path, query or wildcard); http://localhost: is accepted outside production only.
# An invalid entry stops the service at boot. Default: OpenVibe.Live. A robot is embeddable only after its owner
# turns on "embed_public" on its panel. The embedding page must also allow Bot's origin (BASE_URL, https://openvibe.bot)
# in its own CSP frame-src, or the browser refuses the frame.
#BOT_EMBED_ORIGINS=https://openvibe.live,https://www.openvibe.live
# The anonymous read-only /watch socket behind the embed: open sockets per client address and watchers per robot.
#BOT_WATCH_MAX_PER_IP=20
#BOT_WATCH_MAX_PER_ROBOT=500
# OpenVibe Link for Android: the published download (the signed release APK), https on GitHub releases or openvibe.link.
# Unset until the owner's release key exists: /link/phone then says the app is in testing and mints no code.
# LINK_ANDROID_APK_URL=https://github.com/OpenVibers/OpenVibe.Link/releases/download/vX.Y.Z/openvibe-link.apk