Skip to content

Backend configuration

The backend reads its configuration once, at startup, from environment variables. Changes take effect on the next restart. The configuration code is part of the backend: you set variables and mount agent files, you do not write code. If a required variable is missing or a value is invalid, the backend does not start: it logs a server_startup_failed event that names the variable and the problem, never its value, and prints Unable to start HTTP server. to standard error. See Troubleshooting.

  • AMBROGIO_ENV is required in every environment: development, test or production. There is no default.
  • A variable set to an empty string counts as unset.
  • A default applies either in every environment or only in development. In test and production, a variable whose default is development-only is required.
  • Configuration errors name the variable and the condition, for example Missing environment variable AMBROGIO_DATABASE_URL or Invalid environment variable AMBROGIO_HTTP_PORT: expected an integer from 1 to 65535.
Variable Values Default Required in production
AMBROGIO_ENV development, test, production none yes
Variable Values Default Required in production
AMBROGIO_HTTP_HOST Non-empty address to listen on. Use 0.0.0.0 in a container. 127.0.0.1, development only yes
AMBROGIO_HTTP_PORT Integer, 1–65535. The same port serves HTTP and the WebSocket session. 3000, development only yes
AMBROGIO_PUBLIC_HTTPS true or false. Set true when clients reach the backend only over HTTPS, typically through a reverse proxy. Enables the Strict-Transport-Security header. false, development only yes
AMBROGIO_HSTS_MAX_AGE_SECONDS Positive integer: max-age of the Strict-Transport-Security header. none when AMBROGIO_PUBLIC_HTTPS is true
AMBROGIO_ALLOWED_ORIGINS Comma-separated web origins allowed to call the API from a browser, for example https://home.example.com. Each entry is an exact origin: scheme, host and optional port, with no path and no trailing slash. The backend ignores spaces around an entry and empty entries, such as a trailing comma. * is not accepted. unset: no origin is allowed no

AMBROGIO_ALLOWED_ORIGINS affects two things:

  • CORS for the GET endpoints. With no origins, no CORS headers are sent.
  • The WebSocket session. A connection request with an Origin header is accepted only if the origin is in the list; otherwise it is refused with 403. Requests without Origin, which is the case for the terminal client and the satellite, are always accepted. The web chat runs in a browser and always sends the Origin of its page, so that origin must be in the list.
Variable Values Default Required in production
AMBROGIO_DATABASE_URL URL of the PostgreSQL 18 database that stores conversations: postgres://<user>:<password>@<host>:<port>/<database>, or postgresql://. It needs a host, a user, a password and a database name; the port defaults to 5432. It accepts no query parameters, such as ?sslmode=, and no fragment, not even an empty ? or #. The backend ignores the PostgreSQL client variables such as PGPASSWORD or PGSSLMODE and any password file: the URL is the only source. The backend connects without TLS, so the database belongs on the same machine or on your internal network. The user must be able to create tables: the backend creates and updates them at startup. Error messages and logs never show the URL, the user or the password. Any other form stops the backend with Invalid environment variable AMBROGIO_DATABASE_URL: expected a PostgreSQL URL without parameters. none yes, in every environment
AMBROGIO_LANGUAGE en or it: language of the backend’s own messages, agent answers and speech models. Any other value stops the backend from starting. en, every environment no
AMBROGIO_TIME_ZONE IANA time zone of the family, such as Europe/Rome. The agents get the current date and time in this zone; see Current date and time. UTC, every environment no
AMBROGIO_LIST_PAGE_SIZE Positive integer: conversations per page when a client does not ask for a size. 20, every environment no
AMBROGIO_MAX_LIST_PAGE_SIZE Positive integer: largest page a client may ask for. Must be at least AMBROGIO_LIST_PAGE_SIZE. 100, every environment no
AMBROGIO_HISTORY_TURNS Positive integer: how many of the latest turns of a conversation, the new one included, the orchestrator and the agent receive. Older turns stay stored and visible in the clients. 20, every environment no
Variable Values Default Required in production
AMBROGIO_AGENTS_DIR Folder of the agent JSON files. See Agent configuration. /etc/ambrogio/agents, every environment no

The agent files name further variables that hold their credentials, such as MISTRAL_API_KEY. Those variables are required when a file names them.

Voice is disabled when AMBROGIO_VOICE_LANGUAGES is unset or empty: no voice variable is read and no speech model is loaded. Text conversations work either way.

Variable Values Default Required in production
AMBROGIO_VOICE_LANGUAGES Comma-separated list of distinct languages among it and en, for example en,it. It must contain AMBROGIO_LANGUAGE. unset: voice disabled no

Each listed language <L> (EN or IT) has a profile:

Variable Values Required
AMBROGIO_VOICE_<L>_STT_MODEL_DIR Folder of a sherpa-onnx streaming transducer speech recognition model: exactly one file whose name contains encoder and ends with .onnx, exactly one whose name contains decoder and ends with .onnx, exactly one whose name contains joiner and ends with .onnx, and exactly one whose name ends with tokens.txt. All readable. yes
AMBROGIO_VOICE_<L>_MODEL_DIR Folder of a sherpa-onnx Piper voice: exactly one .onnx file, a tokens.txt file and an espeak-ng-data/ folder. All readable. yes

The profile of AMBROGIO_LANGUAGE is the active one: its models are loaded before the server starts listening. The variables of the other listed profile are checked but its models are not loaded. Paths are inside the container and may refer to bundled models or to models mounted into the container. No voice variable has a default.

Speech recognition runs while the client sends the audio: a worker thread decodes each frame as it arrives, using up to two CPU threads, and at input.end only the last part remains; see Limits and timeouts. The model recognises the spoken language by itself; the language of the profile is not passed to it. Real-time recognition needs an x86_64 CPU with AVX2; see Before you install. Speech synthesis uses up to four CPU threads. Audio has the sample rate of the loaded voice.

The backend answers GET /health on the same port as the API, outside /api. The answer has an empty body: 200 while the backend accepts requests, 503 from the moment it starts to stop. The check does not test the database, the voice models, the MCP servers or the model providers, so a 200 tells you only that the backend is running.

The Docker image declares a HEALTHCHECK that requests /health on 127.0.0.1 at AMBROGIO_HTTP_PORT inside the container. Docker reports the container as healthy only while the answer is 200; docker compose ps shows the state. The check reaches the backend on 127.0.0.1, so keep AMBROGIO_HTTP_HOST at 0.0.0.0.

Path Meaning
/etc/ambrogio/agents/ Default folder of agent files, empty in the image.
/opt/ambrogio/models/ Bundled multilingual Nemotron 3.5 ASR streaming 0.6B model, sherpa-onnx-nemotron-3.5-asr-streaming-0.6b-560ms-int8-2026-06-11, for Italian and English speech recognition.
/opt/ambrogio/voices/ Bundled Italian and English Piper voices. A mount at this path replaces them.

The backend repository provides:

File Purpose
compose.example.yaml A production deployment that sets only the variables production requires, plus the bundled voices for English and Italian, and a database service with PostgreSQL 18 (image pgvector/pgvector:pg18, data in a volume on /var/lib/postgresql). It needs AMBROGIO_VERSION, AMBROGIO_DATABASE_PASSWORD and MISTRAL_API_KEY.
examples/agents/*.json Example agent files. See Agent configuration.
Signal Effect
SIGTERM, SIGINT Stops accepting turns, saves turns in progress as interrupted, closes sessions, then the listener, the voice components, the archive and the MCP connections, and exits. Exit code 0 on a clean stop, 1 if a resource could not be closed or the shutdown took longer than 10 seconds. A second signal does not restart the wait.

The backend needs up to 10 seconds to stop. compose.example.yaml sets stop_grace_period: 15s so that Docker does not kill it first; with docker run use --stop-timeout 15, and with docker stop use --time 15.