xixo reads its configuration from the environment. This page lists every variable read by the web
and worker processes, the Rake tasks, and the development tooling. Each section covers one of these
contexts:
Set these the same on the web and worker containers, and restart both after changing one. Values
are strings. A switch is on for 1, true, yes, or on, and off for 0, false, no, off, or
nothing, in any case. xixo refuses to boot when a switch holds any other value.
A production deployment sets all of these. See self-hosting for how to
generate them.
Variable
Default
SECRET_KEY_BASE
none
Signs and encrypts sessions and cookies. Read by Rails. xixo ships no credentials file, so without it Rails raises Missing secret_key_base in production.
ENCRYPTION_PRIMARY_KEY
none
Encrypts the columns that hold secrets: each resource’s credentials, and each tenant’s masks client secret and registration access token. bin/rails db:encryption:init prints a value. Without it, the first read or write of one of those columns raises ActiveRecord::Encryption::Errors::Configuration.
ENCRYPTION_DETERMINISTIC_KEY
none
Encrypts columns that are looked up by value. No column uses it yet, and Rails requires it to be set.
ENCRYPTION_KEY_DERIVATION_SALT
none
Derives the per-column keys from the two above.
XIXO_PUBLIC_ORIGIN
none
The origin browsers and agents use to reach each tenant, with %{subdomain} standing in for the first label of the host, such as https://%{subdomain}.xixo.example.com. A single tenant uses an origin without the placeholder, such as https://xixo.example.com. The origin followed by /mcp is the audience every access token must name, and the sign-in callback is built from it. Production refuses to boot without it. In development and test, xixo builds both from the request’s Host header when it is unset.
MASKS_ISSUER_TEMPLATE
none
The issuer URL of the masks tenant each xixo tenant signs in through, with %{subdomain} standing in as it does in XIXO_PUBLIC_ORIGIN, such as https://%{subdomain}.auth.example.com. With XIXO_TENANT, %{subdomain} in either template is the pinned tenant, whatever host a request names. Unset, sign-in and the /mcp metadata fail with MASKS_ISSUER_TEMPLATE is not set. See signing in.
Declares a single tenant, such as xixo, and serves it at every host.
XIXO_TENANTS
none
Declares several tenants, separated by commas or spaces, such as demo,acme. A request resolves to the tenant named by the first label of its host.
XIXO_HOST_SUFFIX
none, or xixo.test in development
The domain xixo answers under, such as xixo.example.com. Rails accepts that domain and its subdomains and answers 403 to any other Host, except at /up. Unset in production, Rails accepts every host. xixo also refuses an MCP resource whose URL points at a host under this domain, so it cannot call itself.
bin/rails xixo:tenants creates each declared tenant that does not exist, and the container
entrypoint runs it before the server starts. Setting both variables stops that task with
Tenant::TenancyConflict, and the container exits. Setting neither declares no tenants. A request
resolves against the tenants in the database, so removing a name from XIXO_TENANTS neither deletes
that tenant nor stops xixo from serving it. See tenants.
xixo uses four PostgreSQL databases: the primary, a cache, a job queue, and Action Cable. Production
connects to xixo_production, xixo_production_cache, xixo_production_queue, and
xixo_production_cable as the role xixo. That role must hold neither SUPERUSER nor BYPASSRLS,
because tenant isolation depends on row-level security. xixo refuses to enter any tenant when it
connects as such a role.
Variable
Default
POSTGRES_HOST
localhost
The PostgreSQL server’s host name.
POSTGRES_PORT
5432
The PostgreSQL server’s port.
XIXO_DATABASE_PASSWORD
none
The password of the xixo role in production. Unset, xixo connects without a password.
POSTGRES_USER
xixo
The role in development and test. Production ignores it and connects as xixo.
POSTGRES_PASSWORD
xixo
The password in development and test. Production ignores it.
PG_BIN_PATH
none
A directory put first on PATH, so db:schema:dump finds a pg_dump that matches the server’s PostgreSQL version when it writes db/structure.sql.
The OpenSearch server that holds the search index. The index’s alias is xixo_ followed by the Rails environment, and each tenant searches through an alias of its own. See search.
XIXO_EMBEDDING_DIMENSIONS
768
The length of the vectors the index holds. The embedding model a tenant declares must return vectors of this length, and checking an inference resource fails when it does not. Changing it changes the index’s mapping, so RebuildSearchIndexJob builds a new index at 3 a.m. See rebuilding.
XIXO_SEMANTIC_FLOOR
0.55
The lowest cosine similarity a semantic match may have. A match must also score within 0.12 of the best match. Raising it returns fewer, closer matches. See semantic search.
Uploads are staged through Active Storage before a job places them in a resource. See
staging.
Variable
Default
XIXO_STAGING_SERVICE
local
Where production stages uploads. local writes to /rails/storage, which the web and worker containers must share. s3 writes to the bucket named below. Any other name fails when Active Storage loads. Development always uses local.
XIXO_STAGING_BUCKET
none
The staging bucket, when XIXO_STAGING_SERVICE=s3.
XIXO_STAGING_ENDPOINT
none
The endpoint of an S3-compatible service, such as https://s3.example.com. Setting it addresses the bucket by path. Unset, xixo uses AWS.
XIXO_STAGING_REGION
us-east-1
The staging bucket’s region.
XIXO_STAGING_ACCESS_KEY_ID
none
XIXO_STAGING_SECRET_ACCESS_KEY
none
XIXO_FILESYSTEM_ROOTS
none
Directories filesystem resources may use, separated by colons. Each tenant is confined to <root>/<tenant> under each root, and a relative root in a resource resolves under the first one. The files resource that config/resources.yml gives every tenant as default storage is one of these. Unset, every filesystem resource fails its check with no filesystem roots are permitted.
XIXO_GIT_ROOT
none
The directory where git resources keep their shallow bare clones, at <root>/<tenant id>/<resource id>.git. Unset, every git resource is unusable.
xixo refuses to connect to loopback, private, link-local, and other reserved addresses. These
variables open exceptions. Each _ORIGINS variable lists origins separated by commas, such as
http://minio:9000,https://search.internal.example.com. An origin is a scheme, a host, and a port,
and a URL matches when all three match. See addresses and
security.
Variable
Default
XIXO_S3_ORIGINS
none
S3 endpoints an s3 resource may reach at a private address, such as a MinIO container.
XIXO_MCP_ORIGINS
none
MCP servers an mcp resource may reach at a private address.
XIXO_SEARCH_ORIGINS
none
Search endpoints a search resource may reach at a private address, such as a self-hosted SearXNG. XIXO_ALLOW_PRIVATE_FETCH does not apply to search resources.
XIXO_INFERENCE_ORIGINS
none
Every origin an openai_compatible resource may reach, public or private, such as https://api.openai.com,http://ollama:11434. A resource whose base URL is outside the list is unusable, and with the list empty every one is, unless it is reached through a transport.
XIXO_TAILSCALE_SOCKET
none
The socket of a tailscaled that xixo shares a network with, such as /var/run/tailscale/tailscaled.sock. Set, xixo declares a tailnet resource in every tenant beside those in config/resources.yml, and resources reached through it may connect to addresses on the tailnet. See reach a tailnet.
XIXO_ALLOW_PRIVATE_FETCH
false
Lifts the address check for downloads, snapshots, and every resource that fetches URLs. The scheme check stays.
XIXO_GIT_PROTOCOLS
https
The transports git resources may use, separated by commas, such as https,ssh. xixo passes the list to git as GIT_ALLOW_PROTOCOL. Only http and https URLs go through the address check.
The path to a whisper.cpp ggml model file, such as /models/whisper/ggml-base.en.bin. The image carries whisper-cli and no model. Unset, audio and video are analyzed without a transcript, and the analysis logs no transcription model is configured.
XIXO_WHISPER_BIN
whisper-cli
The whisper.cpp command, found on PATH unless it is a path.
XIXO_TRANSCRIBE_SECONDS
3600
How many seconds from the start of a recording are measured and transcribed. Whisper runs at about real time on a CPU.
XIXO_CHROME_PATH
detected
The Chrome or Chromium binary used to render snapshots. Unset, xixo looks in the usual places for the platform. The image includes Chromium. Without a browser, web resources report themselves unusable.
XIXO_CHROME_NO_SANDBOX
false
Starts Chrome with --no-sandbox, for a container that cannot give Chrome the namespaces its sandbox needs.
MCP requests per minute, counted per tenant and per bearer token, or per IP address for a request without one. A request over the limit receives a 429. 0 refuses every request.
XIXO_RUN_BUDGET
20
Syncs, exports, and analyses each subject may start over MCP per clock hour. 0 removes the limit. See budgets and gates.
XIXO_RUN_DEADLINE_HOURS
6
How long a run or an analysis may stay open. Every 10 minutes, ExpireOverdueJob expires any that are past their deadline. 0 sets no deadline.
XIXO_RUN_RETENTION_DAYS
14
How long finished runs are kept. SweepRunsJob deletes older ones at 4:20 a.m. 0 keeps them.
XIXO_AUDIT_RETENTION_DAYS
90
How long audit events are kept. SweepAuditEventsJob deletes older ones at 4 a.m. 0 keeps them.
./bin/jobs runs Solid Queue with two worker pools, set in config/queue.yml. The first takes
the sync, export, and default queues with 5 threads per process. The second takes the
analysis queue.
Variable
Default
BULK_CONCURRENCY
1
Processes for the sync, export, and default queues.
ANALYSIS_CONCURRENCY
1
Processes for the analysis queue.
ANALYSIS_THREADS
4
Threads per analysis process.
ANALYSIS_PER_TENANT
2
Analyses one tenant may run at the same time, across every process, so one tenant cannot fill the pool.
SOLID_QUEUE_IN_PUMA
false
Runs the job supervisor inside the web server, for a small host with no worker container.
XIXO_ITERATORS_DISABLED
false
Closes every gate in every tenant, which stops every sync, analysis, export, and reindex, including ones in progress. See budgets and gates.
The image runs ./bin/thrust ./bin/rails server. Thruster
listens on HTTP_PORT and forwards to Puma on TARGET_PORT.
Variable
Default
RAILS_MAX_THREADS
3
Puma threads per process. Each database connection pool holds this many connections, or enough for an analysis process’s threads, ANALYSIS_THREADS + 5, whichever is more, so setting it for Puma never starves the worker.
WEB_CONCURRENCY
0
Puma worker processes. 0 runs one process without forking. Read by Puma.
PORT
3000
The port Puma listens on. Thruster sets it to TARGET_PORT.
PIDFILE
none
Where Puma writes its process ID.
HTTP_PORT
80
The port Thruster listens on. Read by Thruster.
TARGET_PORT
3000
The port Thruster forwards to. Read by Thruster.
TLS_DOMAIN
none
Has Thruster obtain a certificate from Let’s Encrypt for this domain and serve HTTPS itself. Read by Thruster, which reads more variables than are listed here.
./dev and the compose files read these from your shell. Compose also passes your shell’s
XIXO_TENANT to the containers, defaulting to xixo, and compose.multi.yml clears it and sets
XIXO_TENANTS=demo,acme.
Variable
Default
XIXO_PORT
8180
The host port of the app, at http://xixo.localhost:8180.
XIXO_DOCS_PORT
8181
The host port of the docs site.
XIXO_VITE_PORT
8182
The host port of the Vite dev server.
XIXO_POSTGRES_PORT
5434
The host port of PostgreSQL.
XIXO_OPENSEARCH_PORT
9201
The host port of OpenSearch.
XIXO_MINIO_PORT
9000
The host port of MinIO’s S3 API.
XIXO_MINIO_CONSOLE_PORT
9001
The host port of MinIO’s console.
XIXO_MCP_OAUTH_PORT
8190
The host port of the test MCP server that signs in with OAuth, at http://mcp.localhost:8190. Compose lists it in XIXO_MCP_ORIGINS.
XIXO_MCP_OAUTH_LIFETIME
120
How many seconds the test MCP server’s access tokens last.
XIXO_OLLAMA_PORT
11434
The port of Ollama on the host. Compose points OLLAMA_URL and XIXO_INFERENCE_ORIGINS at it.
XIXO_WHISPER_DIR
~/.cache/whisper
The host directory mounted read-only at /models/whisper.
XIXO_WHISPER_FILE
ggml-base.en.bin
The model file in that directory. Compose sets XIXO_WHISPER_MODEL to it.
MASKS_ISSUER
http://masks.localhost:12345
The masks issuer. Compose sets MASKS_ISSUER_TEMPLATE from it, and compose.multi.yml defaults it to http://%{subdomain}.masks.localhost:12345.
MASKS_CLIENT_DIR
../masks/client
The masks client checkout that compose builds and mounts into the containers.
MASKS_CLIENT_PATH
none
Where the Gemfile finds the masks client. When that directory holds a masks.gemspec, the gem comes from it. Otherwise the gem comes from RubyGems, or from ../masks/client under Gemfile.local. Compose sets it to /masks/client as a build argument.
MASKS_CONTAINER
masks-masks-1
The masks container that ./dev delegation runs its setup in.
MASKS_TENANT
masks
The masks tenant that ./dev delegation signs in through.
DEV_ALLOWED_HOSTS
none
Hosts the Vite and Astro dev servers answer, separated by commas. Set by compose.
DEV_CLIENT_PORT
none
The port the browser uses to reach the Vite or Astro dev server for live reload. Set by compose.
VITE_RUBY_HOST
localhost
Where Rails finds the Vite dev server. Read by vite_ruby, and set by compose.
VITE_RUBY_SKIP_PROXY
false
Read by vite_ruby, and set by compose.
DOCS_SITE
https://xixo.pages.dev
The docs site’s published URL, for canonical links and the sitemap. The docs workflow sets it for preview deploys.
bin/rails db:seed gives each tenant an s3 resource when S3_ENDPOINT is set, and an
openai_compatible resource when OLLAMA_URL is set.
Variable
Default
S3_ENDPOINT
none
The endpoint of the seeded s3 resource. Set, the seeds attach it as items- and the tenant’s subdomain. Compose sets it to http://minio:9000.
S3_REGION
us-east-1
Its region.
S3_ACCESS_KEY_ID
items
Its access key. Compose sets it to xixo.
S3_SECRET_ACCESS_KEY
xixoxixo
Its secret key.
OLLAMA_URL
none
The base URL of an OpenAI-compatible server, such as http://host.docker.internal:11434/v1. Set, the seeds attach it as ollama and make it the default inference resource.
OLLAMA_FAST_MODEL
gemma3:4b
The model for the fast role.
OLLAMA_SMART_MODEL
llama3.1:8b
The model for the smart role.
OLLAMA_VISION_MODEL
gemma3:4b
The model for the vision role.
OLLAMA_AGENT_MODEL
qwen3:8b
The model for the agent role.
OLLAMA_EMBEDDING_MODEL
nomic-embed-text
The model for the embedding role. Its vectors must have XIXO_EMBEDDING_DIMENSIONS dimensions.