Skip to content

Configuration

Team Hub reads a YAML file named server.yaml (by default in the current working directory). Override the path with -c / --config on any CLI subcommand. See CLI — Global options.

Copy the example file to get started:

bash
cp server.yaml.example server.yaml

The canonical example at the repository root is server.yaml.example.

Sections overview

SectionRequiredLive reloadNotes
serverYesNoChanges to host or port require a process restart
dbYesYesReconnects when the raw db mapping changes
redisYesYesReconnects when the raw redis mapping changes
loggingNoNoApplied at process startup; restart after changes
llmNoYesOmit to disable hub-proxied LLM routes
pluginsNoYesOmit to return empty plugin source lists
docsNoYesOptional path to the documentation search index
multitenancyNoNoApplied at process startup; restart after changes
collaborationNoYesDiscussion E2EE mode; reload via SIGHUP or admin reload

Reload triggers while team-hub start is running:

  • SIGHUP to the start process
  • POST /admin/config/reload (admin bearer token required)

See Docker Compose — Using the CLI in the container for details and response shape.

server

HTTP listen settings for the Team Hub API.

KeyTypeRequiredDefaultDescription
portinteger or numeric stringYesTCP port (1–65535)
hoststringYesBind address (e.g. 127.0.0.1, 0.0.0.0)
yaml
server:
  port: 8787
  host: 127.0.0.1

db

Database backend. Set driver to postgres, mysql, or firestore. Driver-specific fields are validated when the server connects.

Postgres

KeyTypeRequiredDescription
driverpostgresYesDatabase driver
hoststringYesDatabase host or Unix socket path
portinteger or numeric stringYesDatabase port (1–65535)
userstringYesDatabase user
passwordstringYesDatabase password (may be empty)
databasestringYesDatabase name
yaml
db:
  driver: postgres
  host: 127.0.0.1
  port: 5432
  user: harbor
  password: harbor
  database: harbor

MySQL

Same fields as Postgres; use driver: mysql and the appropriate port (typically 3306).

yaml
db:
  driver: mysql
  host: 127.0.0.1
  port: 3306
  user: harbor
  password: harbor
  database: harbor

Firestore

KeyTypeRequiredDescription
driverfirestoreYesDatabase driver
projectIdstringYesGCP project id
keyFilenamestringNoPath to a service account JSON key file
yaml
db:
  driver: firestore
  projectId: my-gcp-project
  keyFilename: /path/to/service-account.json

When keyFilename is omitted, Firestore uses Application Default Credentials (workload identity, GOOGLE_APPLICATION_CREDENTIALS, etc.).

redis

Redis is required for authentication throttling. Protected routes return 503 when Redis is unreachable. See Authentication.

KeyTypeRequiredDefaultDescription
hoststringYesRedis host
portinteger or numeric stringYesRedis port (1–65535)
passwordstringNoRedis AUTH password
dbinteger (0–15) or numeric stringNo0Redis logical database index
keyPrefixstringNoPrefix for throttle keys
maxFailuresinteger or numeric stringNo10Failed auth attempts before block
windowSecondsinteger or numeric stringNo900Sliding window for failure counting
blockSecondsinteger or numeric stringNo900Block duration after threshold
yaml
redis:
  host: 127.0.0.1
  port: 6380
  password: redis-secret
  keyPrefix: team-hub:
  maxFailures: 10
  windowSeconds: 900
  blockSeconds: 900

logging

Optional request and error logging via Winston. Applied when the process starts; restart team-hub start after changes.

KeyTypeRequiredDefaultDescription
leveldebug, info, warn, or errorNoinfoMinimum severity written to transports
filestringNoLog file path; omit to disable file output
consolebooleanNotrueWhen true, also write logs to the terminal

Every HTTP request is logged at debug level (method, URL, IP, request id). Unhandled request errors are logged at error level. Set level: debug to see request logs; use info or higher to suppress them while still logging errors.

yaml
logging:
  level: debug
  file: /var/log/team-hub.log
  console: true

llm

Optional hub-proxied LLM access. Omit this section to disable LLM routes. At least one provider apiKey is required when the section is present. User access and monthly token limits are configured via the CLI — see LLM.

KeyTypeRequiredDescription
providers.openai.apiKeystringNo*OpenAI API key
providers.claude.apiKeystringNo*Anthropic API key
providers.gemini.apiKeystringNo*Google Gemini API key
modelsstring arrayNoAllow-list of model ids; omit to offer all catalog models whose provider has a key
mcparrayNoHub MCP servers the agent may call during chat steps (see below)

* At least one provider entry with a non-empty apiKey is required.

llm.mcp

Optional MCP servers Team Hub connects to during POST /llm/chat/step. Tools from these servers are merged into the provider request and executed on the hub; HarborClient never sees hub MCP tool names or results directly.

KeyTypeRequiredDescription
namestringYesDisplay name for logs
urlstringYesMCP server URL (Streamable HTTP or SSE)
headersobject or arrayNoHTTP headers sent with MCP requests

headers accepts any of these shapes (they normalize to key/value pairs):

  • Object map: { "x-api-key": "..." }
  • Array of single-key objects: [{ "x-api-key": "..." }]
  • One nested array level: - [ { "x-api-key": "..." } ]
yaml
llm:
  providers:
    openai:
      apiKey: sk-...
  models:
    - gpt-4o
  mcp:
    - name: Exa
      url: https://mcp.exa.ai/mcp
      headers:
        - [{ 'x-api-key': 'your-exa-key' }]

Supported model ids when using the default catalog:

ProviderModel ids
openaigpt-4o, gpt-4o-mini
claudeclaude-3-5-sonnet-20241022, claude-3-5-haiku-20241022
geminigemini-1.5-pro, gemini-1.5-flash
yaml
llm:
  providers:
    openai:
      apiKey: sk-...
    claude:
      apiKey: sk-ant-...
  models:
    - gpt-4o
    - claude-3-5-sonnet-20241022

plugins

Optional plugin source URLs for HarborClient. Omit this section to return empty lists. Authenticated HarborClient instances merge these read-only endpoints into Settings → Plugins.

KeyTypeRequiredDescription
catalogsURL arrayNoPlugin catalog JSON URLs
trustedURL arrayNoTrusted plugin list JSON URLs

Each entry must be a valid HTTP or HTTPS URL.

yaml
plugins:
  catalogs:
    - https://harborclient.com/plugin_catalog.json
  trusted:
    - https://harborclient.com/plugins/trusted.json

docs

Optional documentation vector search settings for hub-native search_docs. Omit this section to use the Docker default path /app/data/docsSearchIndex.json and other built-in fallbacks.

KeyTypeRequiredDescription
searchIndexPathstringNoPath to serialized docsSearchIndex.json (Orama export)

Rebuild the index in the harborclient repository with pnpm index-docs. The Docker image fetches the latest published index at build time; see Docker Compose — Documentation index.

yaml
docs:
  searchIndexPath: /app/data/docsSearchIndex.json

Hub-native docs search also requires llm.providers.openai.apiKey. Without OpenAI or a readable index file, search_docs is removed from hub chat tool lists.

multitenancy

Optional tenant isolation. Omit this section (or leave enabled: false) for a normal single-tenant install. Every request then uses the reserved default tenant __default__, and clients do not need a tenant header.

KeyTypeRequiredDefaultDescription
enabledbooleanNofalseWhen true, non-default tenants may be selected via X-Harbor-Tenant

When enabled is false, only the default tenant is accepted. A non-default X-Harbor-Tenant header returns 400. When enabled is true, clients may send X-Harbor-Tenant: <tenant-id> to select another tenant created with the CLI (team-hub tenant create). Missing headers still resolve to __default__.

__default__ is reserved: it is created automatically on migrate and cannot be created, renamed, or deleted by operators.

yaml
multitenancy:
  enabled: false

Restart team-hub start after changing multitenancy.enabled. Tenant records are managed with the CLI — see CLI.

collaboration

Optional discussion encryption mode for Team Hub. Plaintext discussions work without this section — see API Endpoints — Discussions for list/create/reply/delete and thread-watch routes. Omit this section (or leave e2ee: false) for normal plaintext discussion bodies.

KeyTypeRequiredDefaultDescription
e2eebooleanNofalseWhen true, discussion create/update routes reject plaintext bodies hub-wide

When e2ee is true:

  • GET /auth/session reports capabilities.discussionE2ee: true
  • Discussion create/update routes accept encryptedPayload objects instead of plaintext body
  • Discussion list responses expose bodyFormat: "encrypted", encryptedPayload metadata, and body: null
  • Collaboration notices omit comment preview snippets
  • HarborClient encrypts comment bodies locally before upload and decrypts list responses when this device is enrolled

Encrypted discussion write bodies use this shape:

json
{
  "encryptedPayload": {
    "ciphertext": "base64-bytes",
    "mlsGroupId": "thread:request:<request-id>",
    "epoch": 0,
    "senderDeviceId": "<device-uuid>",
    "keyFormat": "identity-v1"
  }
}

The server stores ciphertext in the comment body column plus metadata (mlsGroupId, epoch, senderDeviceId, optional commit references) and never decrypts it. HarborClient uses the mls-v1 membership protocol with persisted MLS commits and welcomes for offline catch-up. The older identity-v1 format remains readable for legacy encrypted comments created before Task 4.5.

MLS relay routes (E2EE hubs only):

MethodPathPurpose
POST/discussion-mls/commitsPersist an MLS commit for existing members
GET/discussion-mls/commits?mlsGroupId=List commits for offline catch-up
POST/discussion-mls/welcomesPersist a welcome for a newly added device
GET/discussion-mls/welcomes?mlsGroupId=List welcomes for this device/thread
GET/discussion-mls/group-state/:mlsGroupIdReturn the latest observed MLS epoch

When e2ee is enabled, users enroll devices through POST /devices with public key material only. HarborClient stores private keys locally in encrypted settings and never uploads them. Admins revoke compromised devices with DELETE /admin/device-keys/:id. Revocation and lost private keys can make older encrypted content unrecoverable unless another authorized device can re-add the user in a later MLS release.

yaml
collaboration:
  e2ee: false

Docker environment variables

The all-in-one Docker image renders /etc/team-hub/server.yaml from environment variables on first boot (when the file is missing or empty). Restarts preserve an existing file; mount a host server.yaml to survive container recreation. The CLI does not read TEAM_HUB_CONFIG; pass -c /etc/team-hub/server.yaml explicitly.

VariableDefaultMaps to
TEAM_HUB_HOST127.0.0.1server.host
TEAM_HUB_PORT8787server.port
TEAM_HUB_DB_DRIVERpostgresdb.driver
TEAM_HUB_DB_HOST127.0.0.1db.host
TEAM_HUB_DB_PORT5432db.port
TEAM_HUB_DB_USERharbordb.user
TEAM_HUB_DB_PASSWORDharbordb.password
TEAM_HUB_DB_DATABASEharbordb.database
TEAM_HUB_REDIS_HOST127.0.0.1redis.host
TEAM_HUB_REDIS_PORT6379redis.port
TEAM_HUB_LOGGING_LEVELinfologging.level
TEAM_HUB_LOGGING_FILE/var/log/team-hub/team-hub.loglogging.file
TEAM_HUB_LOGGING_CONSOLEtruelogging.console
TEAM_HUB_MULTITENANCY_ENABLEDfalsemultitenancy.enabled

llm and plugins are not generated from environment variables in the default template. Mount a custom server.yaml or extend deployment tooling for those sections. Logging applies at process startup — restart the container after changing logging env vars. See Deploy.

  • Setup — install, migrate, and start the server
  • Deploy — Docker image, hosting guides, config reload, and env var reference
  • Authentication — bearer tokens and Redis throttling
  • LLM — user access and usage limits