Configuration reference

Every setting in hubzoid.yaml and every environment variable Hubzoid reads, with its type, default and effect, and how they load.

Every setting Hubzoid reads: the deployment file hubzoid.yaml, then environment variables grouped by area. Most Hubs set only the model in <hub>/.env. Everything else has a working default.

Where each setting lives

FileWhat goes in itSection
hubzoid.yamlThe deployment: its Hubs, public address, sign-in, owner, brand, chat defaults, embedded apps and browser editing. Versioned with the Hub folders.hubzoid.yaml
<hub>/.envOne Hub's model, keys and process settings. Readable only by you, never committed.Environment variables
.env next to hubzoid.yamlDeployment secrets: the deployment key, DATABASE_URL, sign-in providers, mail.How settings load
<hub>/restricted/.envSecrets only restricted tools read.How settings load
AWS Secrets ManagerOptional. Any layer above, from a secret.Configuration secrets

A Hub's own declared settings (settings.yaml, hub.settings.yaml) and files such as hitl.yaml and budgets.yaml are described in Hub files.

hubzoid.yaml

The deployment file. It sits in the deployment folder: the folder that holds the Hub folders, or the Hub folder for a deployment of one. hubzoid run with no arguments runs it.

hubzoid.yaml
version: 1
public_url: https://hub.example.com
sign_in: true
owner: sam@example.com
hubs:
  - my-hub
  - finance/finance-hub
KeyTypeDefaultEffect
version1requiredFile format version.
hubslist of foldersrequiredHub folders, relative to this file. Each must contain AGENTS.md. The first also answers at /.
public_urlURLnoneThe address people open, starting with https:// (or http:// on this machine). Fills HUBZOID_PUBLIC_URL.
sign_inbooleanfalseRequire sign-in. Fills HUBZOID_AUTH.
owneremailnoneThe first owner's email. Fills HUBZOID_ADMIN_EMAIL.
in_app_editingbooleanfalseBeta. Let people change agent files from the browser. See browser editing. Fills HUBZOID_IN_APP_EDITING.
brandmappingnoneThe deployment's brand for sign-in, chat, shared pages and Settings. brand.identity.name names the deployment. Check it with hubzoid branding validate.
chatmappingproduct defaultsChat defaults: appearance, locale, time zone and the model picker. See chat defaults.
diagnosticsmappingproduct defaultsDiagnostic limits: check_timeout_seconds, max_parallel_checks, bundle_ttl_minutes, allow_network_probes.
appsmappingnoneApps that embed the chat. See apps: chat inside your app.

An unknown key, a wrong type or a Hub folder without AGENTS.md stops the start, naming the file, key and line. The file holds policy that must never fall back to an open default. A key set both here and in its environment variable, with different values, also stops the start and names both.

Browser editing (Beta)

An agent's instructions, skills, connectors, channels, approval rules, usage limits and settings are files. By default people read them in Settings and change them in the files, or ask Claude Code or Codex. With browser editing off:

  • Proposing and approving Hub changes in Settings or chat answers that it is off.
  • The remember and propose_hub_change tools answer that it is off and say where to change the file.
  • Saving connectors, channels, settings, approval rules and budgets in Settings is read-only, with a note on where to change them.

Turn it on with in_app_editing: true in hubzoid.yaml, or HUBZOID_IN_APP_EDITING=true in one Hub's .env. Files that come from an image (HUBZOID_DATA_DIR set) are never written from the browser. The CLI's apply commands and hubzoid hub-changes keep working either way. Workflow permissions and stored secrets live in the database and stay editable.

VariableTypeDefaultEffect
HUBZOID_IN_APP_EDITINGbooleanfalseThe same switch for one Hub.
HUBZOID_HUB_CHANGES_PUSHbooleanfalsePush the Git commit an approved Hub change makes, as it is: never rebased or forced. Without it the commit stays on the server for someone to push.
HUBZOID_GIT_COMMITTERName <email>the checkout's Git identity, else HubzoidCommitter of those commits. The approving person is the author.

Chat defaults

The chat: section holds the keys of the chat configuration without version:. Settings → Organization shows them read-only. Every agent reads a change within a few seconds. hubzoid chat config schema prints every key with its default.

hubzoid.yaml
chat:
  presentation:
    appearance:
      default_mode: dark
  models:
    allow_choice: true
    default_ref: sonnet
    catalog:
      sonnet: {label: Claude Sonnet, model: sonnet}
      haiku: {label: Claude Haiku, model: haiku}
KeyEffect
presentation.localeDefault and allowed interface language. English is the only one.
presentation.appearanceDefault and allowed modes (light, dark, oled, system), densities and text sizes.
presentation.preferencesWhich of mode, density, text size and time zone people may change.
presentation.timezoneDeployment time zone. Default UTC.
models.catalogModels people may pick in chat, each a label and the model id the agent's runtime accepts. Without a catalog there is no picker, and every reply uses the agent's own model.
models.allow_choicefalse hides the picker again.
models.default_refThe catalog entry picked to start with.

The server refuses a model that is not in the catalog. A model runs on the agent's own runtime, so list models of that runtime: a Claude agent cannot answer with a Codex model. Ratings settings live under feedback. Without a chat: section, chat/config.yaml in the deployment folder is read.

Apps: chat inside your app

Each entry under apps: registers one app that embeds a Hubzoid agent's chat through the AG-UI endpoint /api/agui. The key is the app's client id. Make its secret and a ready entry with hubzoid embed secret.

hubzoid.yaml
apps:
  erpnext:
    name: Kestrel & Oak ERP
    secret_ref: secret:deployment/app-erpnext
    origins: [https://erp.example.com]
    access:
      agents: [my-hub]
      tools: [app_lookup, read_knowledge]
    token_to: [app_lookup]
    max_lifetime: 3600
KeyDefaultEffect
namethe client idDisplay name.
enabledtruefalse refuses the app's passes at once and stops replies it started.
modepasspass: the app's server signs a short pass with the client secret. jwt: Hubzoid checks the app's own JWTs.
secret_refWhere the client secret is, for example secret:deployment/app-erpnext or env:ERPNEXT_CLIENT_SECRET.
originsWeb addresses whose pages may call Hubzoid from a browser.
accessThe most the app may allow: agents and tools. Tools are the agent's tool names, connector:<id>, mcp:<server> or "*". A pass can narrow this, never widen it.
token_toTools that receive the person's own app token.
token_headerAuthorization: BearerHeader a connector receives the token in.
max_lifetime3600Longest a pass may last, in seconds. At most one day.
issuer, audience, jwks_url, public_keyjwt mode: how the app's tokens are checked. audience defaults to hubzoid.
user_claim, name_claim, email_claimjwt mode: which claims name the person.
scopes, all_usersjwt mode: map the token's scope values to access, or give every signed-in user the app's maximum. A token without a mapped scope gets no access.

No Hubzoid account is created for people who come from an app. Their chats are kept under a private id made from the app and their user id. Without a hubzoid.yaml, put the same section in apps.yaml in the deployment folder. The SDK guide chat inside your app (opens in a new tab) has the server and page code.

How settings load

  • <hub>/.env is loaded whenever a command builds or serves the Hub. Its values win over variables already set in your shell. A deployment that should take its values from the process environment instead (a systemd EnvironmentFile, a container platform) ships no .env.
  • <hub>/restricted/.env is loaded next, also overriding. It holds secrets that restricted tools read from the environment. The file tools refuse every path under restricted/, so the model cannot read these values. The claude CLI and the MCP servers it starts do not inherit them, nor Hubzoid's own service secrets.
  • Booleans are on when set to 1, true or yes, in any case. Most also accept on.
  • Numbers that do not parse fall back to the default in almost every case.
  • An empty line value such as OPENAI_API_KEY= sets nothing, so a stored secret can fill it, and hubzoid doctor warns about it.
  • AWS Secrets Manager is optional. A secret named by AWS_SECRET_NAME, HUBZOID_HUB_SECRET_NAME or HUBZOID_RESTRICTED_SECRET_NAME is read at start and wins over the file of its layer. See configuration secrets.
  • With several Hubs, each bridge loads its own Hub's .env, and a Hub's .env stays with that Hub. Settings for the whole deployment belong in hubzoid.yaml and in the .env next to it: the deployment key (HUBZOID_SECRET_KEY), DATABASE_URL, sign-in providers, ENABLE_SIGNUP and mail. That .env wins over the process environment. HUBZOID_PUBLIC_URL, HUBZOID_AUTH, HUBZOID_ADMIN_EMAIL and HUBZOID_IN_APP_EDITING fill a key hubzoid.yaml leaves out. A Hub .env that sets a different HUBZOID_AUTH or HUBZOID_SECRET_KEY stops the start with an error. Sign-in settings (OAUTH_*, OPENID_*, GOOGLE_CLIENT_*, MICROSOFT_CLIENT_*, ENABLE_SIGNUP, ENABLE_LOGIN_FORM, ENABLE_OAUTH_SIGNUP and HUBZOID_PUBLIC_URL) are still taken from the Hubs' .env files when the deployment's own settings lack them, and the start lists them.

hubzoid init writes a starter .env with a random bridge key and commented examples for each provider. hubzoid doctor checks the result and lists each key's layer by name. See doctor.

Models and reasoning

VariableTypeDefaultEffect
MODELmodel idmodel: in AGENTS.md, else claude-localThe Hub's model. claude-local, claude-local/sonnet, claude-local/opus and claude-local/haiku run on the Claude Agent SDK through the installed claude CLI (bare claude-local is Sonnet). codex-local and codex-local/<model-id> run on local Codex through the Codex CLI, version 0.163.0 or 0.147.0. Any other value is a LiteLLM id such as openrouter/anthropic/claude-haiku-4.5, openai/gpt-4o-mini, anthropic/claude-haiku-4-5 or azure/<deployment>, run on the OpenAI Agents SDK.
OPENROUTER_API_KEYsecretnoneKey for openrouter/ models.
OPENAI_API_KEYsecretnoneKey for openai/ models.
ANTHROPIC_API_KEYsecretnoneKey for anthropic/ models. The claude CLI also accepts it for claude-local.
CLAUDE_CODE_OAUTH_TOKENsecretnoneSubscription token for claude-local on a server without an interactive claude login. Create it with claude setup-token. Usage draws on the Claude subscription.
CLAUDE_CODE_OAUTH_TOKEN_SECONDARYsecretnoneOptional first backup for claude-local. When the credential in use is refused for a confirmed usage limit or a rejected login, Hubzoid tries the next one with the same model, Hub and permissions. Needs CLAUDE_CODE_OAUTH_TOKEN set, and is refused together with ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_BASE_URL or a Bedrock, Vertex or Foundry switch.
CLAUDE_CODE_OAUTH_TOKEN_TERTIARYsecretnoneOptional second backup, tried after the secondary.
CODEX_HOMEpath~/.codexWhere Hubzoid reads the file-backed Codex login, auth.json, for codex-local. The Codex CLI's own variable.
HUBZOID_CODEX_BINpathcodex on PATHThe Codex CLI binary codex-local runs. Point it at a private copy of a supported version, for example ~/.hubzoid/codex/node_modules/.bin/codex, to keep your own Codex at another version. A path that is not a file, or an unsupported version, stops the agent with the install command.
JEV_OPENROUTER_API_KEYsecretnoneDedicated OpenRouter key for Jev decisions: hub.call_jev in workflows and the call_jev chat tool. There is no fallback to OPENROUTER_API_KEY, and the chat model never uses this key. The Call Jev capability shows "Jev key missing" in Settings until it is set.
AZURE_API_KEYsecretnoneKey for azure/ models.
AZURE_API_BASEURLnoneAzure OpenAI resource endpoint, for example https://<resource>.openai.azure.com. Required for azure/ models.
AZURE_API_VERSIONstringLiteLLM's defaultAzure REST API version, for example 2024-10-21.
REASONING_EFFORTlow, medium, highunsetOpenAI and Azure reasoning models receive it as their reasoning effort. On Claude it sets an extended thinking budget of 4,000, 12,000 or 24,000 tokens. Other values are ignored.
SHOW_THINKINGoff, indicator, fullindicatorClaude backend only. indicator shows a thinking panel without the text, full streams the summarized reasoning, off shows nothing. true means full and false means off.
SHOW_TOOLSoff, compact, fullcompactHow tool activity appears in replies. compact is the chat app's own row per call and is hidden on Slack, WhatsApp and Telegram. full is an inline line on every surface. off shows nothing. true means compact, false means off, inline means full.
HUBZOID_VISIONbooleantrueSend uploaded images to the model as images. Set false for a text-only or cost-sensitive Hub.
HUBZOID_VISION_MAX_EDGEinteger1568Longest image edge, in pixels, before an image is sent.
HUBZOID_VISION_MAX_IMAGESinteger4Images expanded per turn. Older image references stay as text notes.
HUBZOID_CACHE_TTL1h, 5m1hPrompt cache lifetime for Anthropic-family models routed through LiteLLM (anthropic/, openrouter/anthropic/, Bedrock Claude).
HUBZOID_DISABLE_PROMPT_CACHEbooleanfalseStop adding prompt cache markers to those requests.
HUBZOID_TITLE_MODELmodel idthe Hub's modelModel that writes conversation titles in the Hubzoid web app. Each title is one call, recorded as background usage.
HUBZOID_TEST_RUNTIMEbooleanfalseAllow MODEL=hubzoid-test/<script>, a scripted runtime with no model, for automated tests. Without it that model id is refused.

A scheduled task's model: frontmatter and hubzoid schedule run --model override the model for that run only. See agents and models.

Credential failover keeps the same model and tools on every attempt. A request switches credentials only before any content or tool activity has started, and tries at most three. A limited credential waits for its reported reset, or five minutes without one, and a rejected login stays off until its token is replaced. Hubs that share an operational database share these cooldowns. Several tokens may draw on the same quota, so backups do not guarantee availability, and switching on a real exhausted subscription has not been induced in release testing. Restart the bridges after changing a token. See Claude credential failover (opens in a new tab).

Web app and sign-in

These settings describe the chat UI and how people sign in. With several Hubs, set them once for the deployment, as described in how settings load.

VariableTypeDefaultEffect
HUBZOID_AUTHbooleanfalseRequire sign-in. Off is local mode: every request that addresses this machine is the local owner, admin@localhost, and the public port stays on loopback. Turn it on before anyone else can reach the Hub.
HUBZOID_PUBLIC_URLURLnone, or the loopback address of a local hubzoid runThe address people open, for example https://hub.example.com. Links Hubzoid writes (one-time sign-in links, downloads, report and Settings links) use it, external sign-in callbacks are built on it, and browser changes must come from it or from HUBZOID_ALLOWED_ORIGINS. Required behind a proxy, on any name other than localhost, and for Google, Microsoft or OpenID Connect sign-in.
HUBZOID_ALLOWED_ORIGINScomma listnoneFurther addresses people use, for example https://agents.internal.example.com,http://localhost:3080. Each may send changes from a browser, receives sign-in and connector callbacks on its own address, and counts as this machine in local mode.
HUBZOID_ALLOW_UNAUTHENTICATED_NETWORKbooleanfalseWith sign-in off, hubzoid run --host 0.0.0.0 (or any address that is not loopback) stops with exit code 2. true accepts the risk on a network you trust: anyone who can reach the port uses the Hub as its owner.
HUBZOID_SECRET_KEYsecretgenerated in secret.keyThe deployment key: one or more Fernet keys separated by commas. The first encrypts and signs, every key decrypts, so prepend a new key to rotate. It encrypts personal connection tokens and connector secrets and signs identity assertions between Hubzoid processes. Unset, Hubzoid creates secret.key next to the manifest in the data folder of a deployment of several Hubs, or <hub>/.hubzoid/secret.key for a single Hub. Back it up apart from the data.
HUBZOID_ADMIN_EMAILemailnoneWith the password, creates the first administrator when sign-in is on and no account exists yet, and gives it the owner's access. Remove both after the first start.
HUBZOID_ADMIN_PASSWORDsecretnoneThat administrator's password, 8 to 1024 characters.
HUBZOID_ADMIN_NAMEstringthe email's local partThat administrator's display name.
HUBZOID_LINK_HOURShours72How long a one-time sign-in link stays valid. A link works once.
HUBZOID_SESSION_DAYSdays30Longest life of a session. Lowering it applies to existing sessions too.
HUBZOID_SESSION_IDLE_DAYSdays7A session ends after this many days without use.
HUBZOID_AUTH_MAX_FAILURESinteger10Failed sign-ins within 15 minutes, per client address and per email, before that address or email is locked for 15 minutes. Shared by every bridge.
HUBZOID_PASSWORD_HASH_CONCURRENCYinteger4Password hashes one process computes at once.
ENABLE_SIGNUPbooleanfalseShow Create an account on the sign-in page. A new password account waits for an administrator's approval.
ENABLE_OAUTH_SIGNUPbooleanfalseCreate an account at a person's first external sign-in. It waits for approval unless the provider verified the email and its domain is listed explicitly in OAUTH_ALLOWED_DOMAINS.
ENABLE_LOGIN_FORM, ENABLE_PASSWORD_AUTHbooleantrueEither set to false turns password sign-in off, so people use an external provider only.
HUBZOID_MFA_REQUIREDnone, admins, alladminsWho must use two-step sign-in until an administrator picks a rule in Settings → Organization → Sign-in (or hubzoid auth mfa policy). Someone who must use it sets it up right after their password: an authenticator app code, then ten one-time recovery codes. People who sign in through SAML use their provider's own policy.
HUBZOID_SUPPORT_CONTACTtextyour administratorWho people are told to ask for access, on My access and in sign-in help.
HUBZOID_MAX_FILES_PER_MESSAGEinteger10Files a person may attach to one message in the web app.

Sign-in providers

VariableTypeDefaultEffect
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETstring, secretnoneTurn on Continue with Google. Register <HUBZOID_PUBLIC_URL>/oauth/google/callback as the redirect URI.
GOOGLE_OAUTH_SCOPEstringopenid email profileScopes asked of Google.
MICROSOFT_CLIENT_ID, MICROSOFT_CLIENT_SECRETstring, secretnoneTurn on Microsoft (Entra ID) sign-in. The redirect URI is /oauth/microsoft/callback. A Microsoft email counts as verified only when the ID token carries the xms_edov claim.
MICROSOFT_CLIENT_TENANT_IDstringcommonThe Entra ID tenant.
MICROSOFT_OAUTH_SCOPEstringopenid email profileScopes asked of Microsoft.
OPENID_PROVIDER_URLURLnoneDiscovery document or issuer URL of one standard OpenID Connect provider. With OAUTH_CLIENT_ID it turns that provider on. The redirect URI is /oauth/oidc/callback.
OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRETstring, secretnoneThe OpenID Connect client. Leave the secret empty for a public client, which uses PKCE only.
OAUTH_PROVIDER_NAMEstringSSOThe button reads "Continue with" and this name.
OAUTH_SCOPESstringopenid email profileScopes asked of the OpenID Connect provider.
OAUTH_ALLOWED_DOMAINScomma listanyEmail domains that may sign in with any external provider. Judged on a verified email only. Unset or * allows any domain. A listed Google Workspace domain must also be the account's Workspace domain.
OAUTH_MERGE_ACCOUNTS_BY_EMAILbooleanfalseLet an external sign-in with a verified email attach to the existing account with that email, such as one created in Settings. Google sign-in only accounts need it, and hubzoid doctor warns (auth.google_merge) when Google is configured without it.

See authentication for setting up sign-in and the first administrator.

VariableTypeDefaultEffect
PORTinteger3080Public port: the web app, downloads and MCP. --port wins.
BRIDGE_PORTintegerthe first free port from 8000Bridge port. The bridge always binds 127.0.0.1. --bridge-port wins. Unset, hubzoid run skips ports in use. A port you set must be free.
HUBZOID_HOSTaddress127.0.0.1Interface the public port binds to. 0.0.0.0 listens on every interface and needs sign-in. --host wins.
BRIDGE_API_KEYScomma listdevKeys the bridge accepts as Authorization: Bearer on /v1 and /uploads. Hubzoid's own processes, such as the Slack adapter and the inbound receiver, send the first key. dev is public, so set a random key for any shared install. hubzoid init writes one. dev is never accepted on /artifacts.
MODEL_LABELstringslug of the AGENTS.md nameModel id reported by /v1/models. Hubs that run together need different ones.
HUBZOID_MAX_UPLOAD_BYTESinteger26214400 (25 MiB)Per-file cap for attachments in chat requests and for /uploads. A larger file gets HTTP 413.
HUBZOID_ARTIFACT_SECRETsecretgenerated in .hubzoid/artifact_secretSecret that signs download links. Set it only to share one secret across hosts serving the same Hub. Changing or deleting it revokes every issued link.
HUBZOID_ARTIFACT_LINK_TTLseconds7 daysHow long a newly issued chat download link from write_artifact stays valid. 0 means links do not expire. In the web app, the signed-in owner of a conversation can always download its files.
HUB_LOG_LEVELinfo, debug, warninginfoLog level of the bridge and edge.
HUBZOID_INBOUND_PORTinteger8100Loopback port of the WhatsApp, Telegram and generic webhook receiver. With several Hubs, a Hub without one gets the next free port.
HUBZOID_HUB_SLUGstringslug of the Hub folder namePath segment in /webhooks/<slug>/. Set it only when two Hubs that run together would otherwise get the same slug.
HUBZOID_SESSION_IDstringrandomFixes the session id used for the output/<session>/ folder.
HUBZOID_DATA_DIRpaththe Hub folderWhere a Hub writes its state: .hubzoid/ (databases, keys, chats, artifacts), output/, logs/ and .inbound/. Set it to a durable volume such as /data when the Hub's own files are baked into an image from Git. The Hub folder is then only read, and Settings shows its files read-only. One Hub per data folder, run on its own.
HUBZOID_TRUSTED_PROXIEScomma listloopback, and private networks while unsetProxies in front of the public port whose X-Forwarded-For is believed: addresses or networks, or * when the port is reachable only through your proxy. List a CDN such as Cloudflare, or every person counts as one of its few addresses.

hubzoid run opens the web app in a browser only from a terminal on a loopback host, and on Linux only in a graphical session (DISPLAY or WAYLAND_DISPLAY). The standard BROWSER variable picks the browser. --no-open skips it.

Artifacts and email

These settings govern published artifacts, created with hub.publish_artifact or the markdown task tool publish_artifact, and workflow email, sent with hub.send_email or send_email. See the workflow API.

VariableTypeDefaultEffect
HUBZOID_ARTIFACT_MAX_BYTESinteger52428800 (50 MiB)Largest file a workflow may publish as an artifact.
HUBZOID_ARTIFACT_LINK_DAYSinteger7Default lifetime, in days, of an artifact's public link. Kept between 1 and 90.
HUBZOID_ARTIFACT_ALLOW_ORIGINScomma listnoneHTTPS origins that published HTML artifacts may load scripts, styles, fonts and images from, for example https://cdn.jsdelivr.net. Unset, HTML artifacts make no network requests.
HUBZOID_SHARE_DOMAINScomma listnoneEmail domains for company links (audience="domain" and the viewer's Share). Anyone at a listed domain can open such a link after company sign-in, as a reader-only account with no agent access. Company links stay off unless Google, Microsoft or OpenID Connect sign-in is set up with ENABLE_OAUTH_SIGNUP=true and each share domain listed explicitly in OAUTH_ALLOWED_DOMAINS.
HUBZOID_EMAIL_DELIVERYsmtp, previewsmtppreview writes each email to <hub>/.hubzoid/outbox/<person>/ and reports that nothing was sent.
HUBZOID_SMTP_HOSThostnoneSMTP server. Required with HUBZOID_SMTP_FROM for smtp delivery.
HUBZOID_SMTP_PORTinteger587, or 465 with HUBZOID_SMTP_SSLSMTP port.
HUBZOID_SMTP_USERNAMEstringnoneSMTP user name, when the server needs one. Credentials are sent only over TLS.
HUBZOID_SMTP_PASSWORDsecretnoneSMTP password.
HUBZOID_SMTP_FROMemailnoneSender address. A single email address.
HUBZOID_SMTP_STARTTLSbooleantrueUpgrade the connection with STARTTLS. Certificates and host names are verified.
HUBZOID_SMTP_SSLbooleanfalseUse implicit TLS instead, usually on port 465.
HUBZOID_SMTP_TIMEOUTseconds30Time allowed for each SMTP operation.

Email goes to the account the run acts as, or, from a team run, to the addresses of an email list declared in workflows/settings.yaml. Code and models cannot name any other recipient. A local or example account such as admin@localhost cannot receive email, so run the workflow as a real account or use preview. SMTP delivery over TLS is tested against stand-ins in release testing, not end to end against a real mail server. With several Hubs, these settings can be set once for the deployment, in the .env next to hubzoid.yaml, and overridden per Hub.

Databases and deployment state

VariableTypeDefaultEffect
DATABASE_URLdatabase URLsqlite:///<hub>/.hubzoid/hub.dbDatabase for the Hub's own tables. A PostgreSQL URL here is also used for the operational store and the workflow engine unless the variables below say otherwise. postgresql:// URLs use psycopg, which Hubzoid installs.
HUBZOID_OPERATIONAL_DBdatabase URLsee belowShared store for accounts, sessions, access grants, identities, connections, audit records, usage and workflow state.
HUBZOID_DBOS_DBdatabase URLsee belowSystem database of the durable workflow engine. SQLite is always per Hub. PostgreSQL can be shared by several bridges.
HUBZOID_DEPLOYMENTpathdiscovered from <hub>/.hubzoid/deployment.jsonExplicit path to the manifest of a deployment of several Hubs.

The operational store resolves in this order: the registered deployment manifest, then HUBZOID_OPERATIONAL_DB, then DATABASE_URL, then the Hub's own database. The workflow engine resolves as: the manifest entry for the Hub, then HUBZOID_DBOS_DB, then DATABASE_URL when it is not SQLite, then <hub>/.hubzoid/dbos.db. When a manifest exists, a different HUBZOID_OPERATIONAL_DB or HUBZOID_DBOS_DB stops startup with an error instead of splitting the data. Change the deployment's configuration, not a single bridge.

Backups

VariableTypeDefaultEffect
HUBZOID_BACKUPSweekly, daily, offweeklyScheduled verified backups of the deployment.
HUBZOID_BACKUP_KEEPinteger4Scheduled backups kept. Older ones that still verify are removed.
HUBZOID_BACKUP_DIRpath<data>/.hubzoid/backupsWhere scheduled backups go.
HUBZOID_PG_BINpathPATHFolder holding pg_dump and pg_restore, when they are not on PATH.

See backup and restore.

Chat surfaces

Slack

VariableTypeDefaultEffect
SLACK_BOT_TOKENsecretnoneBot user OAuth token (xoxb-). Required by the Slack adapter.
SLACK_APP_TOKENsecretnoneApp-level token (xapp-) with the connections:write scope, for Socket Mode. Required by the Slack adapter.
SLACK_IDENTITY_MAPPINGbooleanfalseResolve each sender's verified Slack email (needs the users:read.email scope) and act as the account with that email, matched without regard to case. An email without an active account is treated as nobody. Restricted tools also need the Slack surface in HUBZOID_RESTRICTED_SURFACES.

WhatsApp and Telegram

VariableTypeDefaultEffect
WHATSAPP_VERIFY_TOKENsecretnoneToken checked on Meta's webhook verification request.
WHATSAPP_APP_SECRETsecretnoneApp secret used to verify the X-Hub-Signature-256 header on each delivery.
WHATSAPP_TOKENsecretnoneAccess token used to send replies.
WHATSAPP_PHONE_NUMBER_IDstringnoneSending phone number id. All four WhatsApp variables are required.
TELEGRAM_BOT_TOKENsecretnoneBot token.
TELEGRAM_WEBHOOK_SECRETsecretnoneChecked against the X-Telegram-Bot-Api-Secret-Token header. Both Telegram variables are required.
TELEGRAM_STREAMbooleantrueEdit the Telegram reply in place as it streams.
INBOUND_STREAM_INTERVALseconds1.0Minimum time between edits of the same Telegram message. Values below 0.8 are raised to 0.8.
INBOUND_HISTORY_MAXinteger40Messages of history kept per WhatsApp or Telegram conversation.
INBOUND_HISTORY_TTL_DAYSnumber0Drop a chat's history older than this many days when that chat saves a new turn. Old turns are still read for the first reply after a long gap. 0 keeps it.
INBOUND_MSG_VERIFY_PROMPTtextPlease tap the button below to verify your number.Fixed message asking a sender to verify.
INBOUND_MSG_VERIFIEDtextYou are verified. How can I help?Fixed message after verification.
INBOUND_MSG_NOT_REGISTEREDtextThis number is not registered for access.Fixed message for a number that no person in Settings and no Hub roster has.
INBOUND_MSG_NOT_OWN_CONTACTtextPlease share your own number to verify.Fixed message when a sender shares someone else's contact.
INBOUND_MSG_PLEASE_VERIFYtextPlease verify first. Tap the button below to share your number.Fixed message for an unverified sender.
INBOUND_MSG_NO_RESPONSEtextSorry, I do not have a response for that. Please try again.Sent when a reply would otherwise be empty.

Generic webhook

VariableTypeDefaultEffect
WEBHOOK_INBOUND_SECRETsecretnoneShared secret. The generic webhook receiver is off without it.
WEBHOOK_INBOUND_NAMEstringwebhookPath segment of the endpoint, /webhooks/<hub>/<name>, and the inbox a markdown task names in on_webhook:.
WEBHOOK_INBOUND_HMACbooleanfalseVerify X-Signature-256 or X-Hub-Signature-256 as sha256= HMAC of the raw body instead of accepting the secret as a header.

Webhooks that start code workflows are declared in workflows/settings.yaml and take their secrets from WEBHOOK_SECRET_<NAME>, listed under schedules and workflows. See WhatsApp and Telegram, Slack and webhooks.

Access and identity

VariableTypeDefaultEffect
HUBZOID_RESTRICTED_SURFACEScomma listweb,api,mcp,workflow,appThe complete list of surfaces that may reach restricted tools and personal connections. app is chat embedded in your own app. owui, the older chat, is also in the default. Setting it replaces the default, so include the defaults you still want, for example web,api,mcp,workflow,app,slack-dm. slack-dm covers one-to-one Slack conversations. Add whatsapp or telegram to reach restricted tools and personal connections from those surfaces. Never add slack-channel, where one reply answers a shared thread under one person's identity.
HUBZOID_ACCESS_TOOLSbooleanonfalse removes the access tools (my_management_scope, who_has_access, explain_access, propose_access_change, propose_new_account) from this agent. Otherwise they are hidden until an organization administrator grants Manage access from chat (access_tools). A proposal applies only after the same manager confirms it in Settings.
HUBZOID_MANAGEMENT_TOOLSbooleanunsetDeprecated. true gives every access manager the access tools without the access_tools grant, and hubzoid doctor warns. false removes the tools, like HUBZOID_ACCESS_TOOLS=false. Grant Manage access from chat instead, then remove it.
HUBZOID_CHANGE_REQUEST_TTLseconds900How long a proposed change waits for confirmation in Settings. Kept between 60 and 86,400.
HUBZOID_PORTAL_DEVbooleanfalseDevelopment only. With HUBZOID_PORTAL_DEV_USER, Settings trusts that subject without a sign-in session. Never set it on a shared or public deployment.
HUBZOID_PORTAL_DEV_USERemailnoneThe subject trusted when HUBZOID_PORTAL_DEV is on.

See identity and access and built-in tools.

Schedules and workflows

VariableTypeDefaultEffect
HUBZOID_SCHEDULESbooleanfalseTurn on code workflows (workflows/<name>/*.py) in a hubzoid run of one Hub: they load, their schedules fire, and people can start them from Settings or chat. Without it, a Hub that declares webhooks in workflows/settings.yaml still loads code workflows for webhook runs, but schedules do not fire. A hubzoid run of several Hubs turns it on for every Hub. Markdown tasks and scheduled evals do not need it.
HUBZOID_DISABLE_SCHEDULEbooleanfalseStop markdown tasks (schedule/*.md) and scheduled evals from firing.
HUBZOID_WORKFLOW_USERemailthe owner recorded at setup, admin@localhost locallyThe account team workflows and markdown tasks run as when they declare no run_as. A Hub's value (its secret, then its .env) wins over the deployment's. It must be an existing account that is not pending or blocked and holds Use this agent.
HUBZOID_WORKFLOW_TOOLSbooleanonfalse removes the workflow tools (list_workflows, workflow_runs, run_workflow, pause_workflow, resume_workflow, cancel_workflow_run) and the personal schedule tools from this agent. Otherwise they are hidden until someone is granted their capabilities in Settings.
WEBHOOK_SECRET_<NAME>secretnoneSecret of the workflow webhook <name> declared in workflows/settings.yaml, upper case with - written as _. A webhook without its secret refuses every delivery.
HUBZOID_ALERT_URLURLnoneFallback alert destination: a webhook URL that receives workflow alerts when no other destination is set: not the workflow's alert_to= or on_failure=, the markdown task's alert_to:, nor alerts.to in workflows/settings.yaml.
HUBZOID_ALERT_SECRETsecretnoneSigns webhook alerts. Each POST then carries X-Hubzoid-Timestamp and X-Hubzoid-Signature: sha256=<hex>, the HMAC-SHA256 of the timestamp, a . and the body. Slack and email alerts are not signed.
HUBZOID_EVAL_JUDGE_MODELmodel idthe Hub's modelModel that grades eval cases. hubzoid eval run --judge-model wins. See evals.
HUBZOID_EVAL_KEEP_RUNSinteger200Eval result files kept per Hub. 0 keeps every run. A negative value is an error.

Personal schedules and Run for me runs act as the person who saved or started them, never as HUBZOID_WORKFLOW_USER. Other workflow limits, such as max_concurrent_workflows, agent_max_attempts, webhook declarations, email lists and alert rules, live in workflows/settings.yaml rather than the environment. See the workflow API and workflows and schedules.

A Hub's own workflow code can read more variables. A ready-to-use Hub lists them in its README and, when it declares them, in its settings.yaml, which hubzoid hub settings shows.

MCP and connectors

VariableTypeDefaultEffect
MCP_SERVERbooleanon for a local hubzoid run and for an https HUBZOID_PUBLIC_URL, off otherwiseServe the Hub's tools and knowledge over MCP at /mcp (at /b/<hub>/mcp with several Hubs). Clients connect with OAuth after the person signs in and consents. A value set in the Hub's .env wins. With several Hubs, set it in the Hub's .env with MCP_PUBLIC_URL.
MCP_PUBLIC_URLURL<public address>/mcp when MCP_SERVER is on by defaultThe canonical public MCP endpoint, required with MCP_SERVER=true. It must use https (plain http only on loopback) and end in /mcp, or in /b/<slug>/mcp with several Hubs.
HUBZOID_MCP_TIMEOUTseconds30Time one MCP tool call may take, for every MCP server and connector. A server's own client_session_timeout_seconds in .mcp.json wins. On claude-local an explicit value becomes the CLI's MCP_TOOL_TIMEOUT, unless that is set.
HUBZOID_CONNECT_JOURNEYbooleantrueGive the agent the connect_account tool, which sends a person a personal link to connect an app from chat or WhatsApp. false removes it.
HUBZOID_CONNECT_TTLseconds600How long a connection link stays valid. Kept between 60 and 3,600.
HUBZOID_CONNECTOR_PRIVATE_HOSTScomma listnonePrivate hosts a connector's sign-in may reach besides the server's own host, for example an internal identity provider. Cloud metadata addresses are always refused.

Connectors themselves are files, not environment variables: connectors/.mcp.json for servers everyone shares, and connectors/<id>.yaml for remote servers people sign in to (see Hub files). Settings can save them only when browser editing is on. See add a connector and connect an assistant.

Tools and the shared browser

VariableTypeDefaultEffect
HTTP_ALLOWLISTcomma listnoneHostnames http_get may request. Subdomains of a listed host are allowed. Only the requested URL is checked, not where it redirects. Unset allows any host.
HUBZOID_DISABLE_HTTP_GETbooleanfalseRemove http_get from the tool registry.
HUBZOID_DISABLE_WEB_SEARCHbooleanfalseRemove web_search from the tool registry.
CONNECTIONScomma listnoneApps this Hub's own tools may connect per person through the optional Composio broker, for example odoo,slack. Unset turns these connections off.
COMPOSIO_API_KEYsecretnoneKey for that broker. Required for CONNECTIONS.
HUBZOID_BROWSERbooleanfalseGive the Hub one shared Playwright browser as MCP tools (browser_navigate, browser_click, browser_snapshot and the rest).
HUBZOID_BROWSER_PORTinteger8931Port of the browser sidecar Hubzoid starts itself.
HUBZOID_BROWSER_MCP_URLURLhttp://localhost:<port>/mcpConnect to an already running Playwright MCP endpoint and start nothing.
HUBZOID_BROWSER_CDP_URLURLnoneAttach the sidecar to a browser pool (browserless) for hard limits. Use the ws://host:3000?token=... form so the token survives.
HUBZOID_BROWSER_CHANNELstringchromiumBrowser build for the sidecar. Keep chromium in containers.
HUBZOID_BROWSER_MCP_VERSIONstring0.0.81Pinned @playwright/mcp version for the sidecar Hubzoid starts. latest floats deliberately.
HUBZOID_BROWSER_MAX_RSS_MBinteger0Without a pool, restart the browser when its memory passes this many MB. 0 turns the watchdog off.
HUBZOID_BROWSER_CONCURRENTinteger2Parallel browser slots in the pool. Read by docker/browser-compose.yml.
HUBZOID_BROWSER_QUEUEDinteger5Requests that may wait before the pool rejects new ones. Read by the compose file.
HUBZOID_BROWSER_MEMORYsize2gHard memory ceiling of the pool container. Read by the compose file.
HUBZOID_BROWSER_TIMEOUT_MSmilliseconds60000Pool timeout that reaps stuck sessions. Read by the compose file.
HUBZOID_BROWSER_TOKENsecrethubzoidToken shared by the pool and the sidecar in the compose file.
HUBZOID_BROWSER_MCP_IMAGE_TAGstringv0.0.81Playwright MCP image tag in the compose file.
HUBZOID_BROWSER_TIMEOUTseconds60No effect. Hubzoid reads the value, but nothing uses it. Set the pool timeout with HUBZOID_BROWSER_TIMEOUT_MS.

See tools and connectors and built-in tools.

Telemetry

VariableTypeDefaultEffect
HUBZOID_OTEL_ENDPOINTURLnoneOTLP/HTTP base URL for traces, for example a Langfuse .../api/public/otel endpoint. Unset turns tracing off.
HUBZOID_OTEL_NORMALIZEbooleanfalseclaude-local only. The bridge receives the claude process's traces on a loopback route, renames token attributes to the gen_ai.usage.* names Langfuse maps, sets the person as user.id, and forwards them. No separate collector is needed.
HUBZOID_OPENAI_TRACINGbooleanfalseOpenAI Agents backend only. Export runs, including prompts and tool data, to OpenAI's trace dashboard. Independent of HUBZOID_OTEL_ENDPOINT.
OTEL_EXPORTER_OTLP_HEADERSk=v,k=vnoneHeaders for the trace backend, such as Authorization=Basic .... Also read back for eval pushes to Langfuse.
OTEL_EXPORTER_OTLP_PROTOCOLstringhttp/protobufOTLP protocol for the claude process.
OTEL_METRICS_EXPORTERstringnoneMetrics exporter for the claude process. Set it only if you run a metrics backend.
OTEL_LOGS_EXPORTERstringnoneLogs exporter for the claude process.
OTEL_RESOURCE_ATTRIBUTESk=v,k=vnoneResource attributes for traces. On the OpenAI Agents backend Hubzoid adds hubzoid.hub to yours. On claude-local the claude process gets hubzoid.user, hubzoid.hub, hubzoid.surface and user.id for each turn.
LANGFUSE_PUBLIC_KEYsecretnoneWith LANGFUSE_SECRET_KEY and HUBZOID_OTEL_ENDPOINT, eval runs are pushed to Langfuse as datasets with scores.
LANGFUSE_SECRET_KEYsecretnoneSee LANGFUSE_PUBLIC_KEY.

See observability.

Several Hubs

VariableTypeDefaultEffect
HUBZOID_GATEWAY_BRANDINGHub slug or pathsee effectThe Hub whose branding/ folder brands the deployment's pages, or a folder that holds branding/. Without it, <data-dir>/branding/ is used when it holds files, then the first Hub's branding/.
HUBZOID_GATEWAY_BRIDGE_TIMEOUTseconds180How long the deployment waits for each bridge's first answer. Raise it when many Hubs start at once on a busy machine.

Sign-in and the deployment key are set once for the deployment, as described in how settings load. See several Hubs.

Remote CLI

VariableTypeDefaultEffect
HUBZOID_SERVERURLnoneThe deployment hubzoid auth and hubzoid api talk to. --server wins.
HUBZOID_API_KEYsecretnoneA personal API key (hzp_...) those commands sign in with instead of a saved session.

Docker Compose

docker/docker-compose.yml in the SDK repository reads these from your shell.

VariableDefaultEffect
HUB_PATHrequiredAbsolute path of your Hub folder, mounted at /hub. The compose file stops with a message when it is not set.
HUBZOID_PUBLISH3080The published port, for example 127.0.0.1:3080 to stay on this machine.
HUBZOID_IMAGEhubzoid:localThe image to run.
HUBZOID_AUTHtrueSign-in. The compose file turns it on by default.
Terminal
HUB_PATH=$PWD/my-hub docker compose -f docker/docker-compose.yml up -d

See Docker.

Configuration secrets

Each configuration layer can also read an AWS Secrets Manager secret that holds a flat JSON object of settings. Nothing here is needed when you use files alone. With no secret named, Hubzoid makes no AWS call. This path is tested against stand-ins in release testing, not end to end against AWS itself.

VariableLayerWhere to set itEffect
AWS_SECRET_NAMEDeploymentThe deployment's environment or the .env next to hubzoid.yaml, or a single Hub's .env or environmentName or ARN of the deployment secret. A deployment of several Hubs records it in deployment.json so bridges started on their own find it. With several Hubs, a Hub's .env naming it is ignored.
AWS_REGIONAllBeside the secret nameRegion for every secret read. AWS_DEFAULT_REGION is the fallback, and an ARN carries its own region.
HUBZOID_HUB_SECRET_NAMEHub<hub>/.env onlyA secret whose keys override <hub>/.env for this Hub.
HUBZOID_RESTRICTED_SECRET_NAMERestricted<hub>/restricted/.env onlyA secret whose keys override restricted/.env for this Hub's restricted tools.
  • Secrets are read at start with boto3's normal credential chain: an instance or task role, AWS_PROFILE, or access keys in the environment.
  • Within a layer the secret wins over the file. A single Hub applies its deployment secret after its .env. A Hub that runs with others takes only the bridge's share of the deployment secret, before its own .env. That share covers workflow identity, email, report link settings, database URLs, the public address, HUBZOID_SECRET_KEY and telemetry.
  • A secret may not set AWS_* keys, secret names or process control keys such as PATH, LD_* or PYTHONPATH. Values are strings, numbers or booleans.
  • A secret that cannot be read stops the process at start with the secret name, the layer and the AWS error class, never a value.
  • Values are read once. After a rotation, restart what reads the secret: the whole hubzoid run for the deployment secret, and the Hub, with hubzoid restart <hub>, for a Hub or restricted secret.
  • hubzoid doctor reports each configured key's layer by name only, and whether each secret can be read. --skip-secret-fetch lists the secrets without calling AWS. See doctor.

No longer read

MCP_ACCESS_GROUP has no effect. Access to MCP is a Use this agent grant in Settings, and personal MCP connections are connectors added in Settings. hubzoid doctor warns about a leftover MCP_ACCESS_GROUP. Remove it from older .env files.

Set by Hubzoid

Hubzoid sets these for its own child processes. You do not set them by hand.

VariableSet byPurpose
HUBZOID_HUB_DIRhubzoid runTells the bridge which Hub to serve.
HUBZOID_GATEWAYa hubzoid run deployment from hubzoid.yaml or of several HubsMarks a bridge as one of several Hubs run together, which also turns on code workflows.
HUBZOID_EDGE_DEFAULT, HUBZOID_EDGE_DEFAULT_FALLBACKS, HUBZOID_EDGE_ROUTEShubzoid runThe edge's default upstream, the bridges it tries next, and its table of forwarded path prefixes.
HUBZOID_EDGE_PUBLIC_SCHEMEhubzoid runhttps when the declared public URL is HTTPS, so the edge forwards X-Forwarded-Proto: https upstream.
HUBZOID_EDGE_WORKFLOW_HUBShubzoid runThe Hubs whose workflow engines the edge watches for /healthz/workflows.
HUBZOID_WEBHOOK_EVENTSthe markdown task runnerNewline-separated event files a webhook-triggered run: script owns for this run.
HUBZOID_RUN_ASthe markdown task runnerThe account a run: script's run acts as. For information only. It grants the script nothing.
HUBZOID_DEPLOYMENT_SECRET_INHERITEDa hubzoid run of several HubsTells a bridge that hubzoid run has already read the deployment secret for it.
MCP_TOOL_TIMEOUTthe claude-local runtimeThe claude CLI's MCP tool timeout, from an explicit HUBZOID_MCP_TIMEOUT.

Next steps

Choose which cookies Hubzoid can use. You can change this at any time from Cookie settings in the footer. Read the Cookie Policy for details.