---
title: Configuration and data locations
description: Know which settings Teloa manages, which belong to the DSH agent engine, and where your data lives.
lastVerified: 2026-10-22
---

# 5.1 Configuration and data locations

The current source workflow runs Teloa and DSH in local Node.js processes, with only PostgreSQL in Docker by default. Compose runs all three in containers. Both existing workflows use standard Docker; OrbStack is not required. Native npm installation is described below; keep data directories separate between installation methods.

## Where files and programs are used

Native mode uses a local working directory and integrated, authorized tools. Compose uses programs and a working directory inside the container. The default Compose configuration does not mount your computer's entire filesystem and cannot directly invoke native desktop applications.

To expose a directory on your computer to a container, configure an explicit mount and its read/write permissions. The path belongs to the machine running the Docker engine; a remote engine does not automatically receive files from your computer. See [Docker bind mounts](https://docs.docker.com/engine/storage/bind-mounts/).

## Who manages each setting

| Setting | Managed in | What to check |
| --- | --- | --- |
| Business areas, employees, tasks and grants | Teloa pages and database | Use the supported actions; direct database edits bypass validation |
| Models, keys, MCP and run configurations | The DSH configuration reached from Settings | Verify a real call after saving |
| Extension composition | Source configuration and `setup:dsh` | Prepare and build again after changes |
| Browser authentication | A new authentication link from the launcher | The link changes after restart; do not publish it |

## Compose data locations

| Named volume | Container path | Contents |
| --- | --- | --- |
| `app-data` | `/data` in the app | DSH conversations, model configuration, Teloa working files and knowledge |
| `postgres-data` | `/var/lib/postgresql/data` in the database | PostgreSQL business data |
| `database-secret` | `/run/teloa-secrets` in the app and database | The generated database password |
| `credentials-secret` | `/run/teloa-credentials` in the app only (read-only) | The generated key storage master key, kept apart from `app-data` |

Actual volume names include the Compose project prefix, such as `teloa_app-data`. Using `-p` changes that prefix. Do not confuse these volumes with the fixed container used for source development.

DSH configuration lives in `/data/dsh`, Teloa data in `/data/teloa`, and the working directory in `/data/teloa/workspace`. The database is reachable through the internal Compose service `db`; no database port is published on the device running Teloa.

## Source development data locations

When you run Node.js directly from source, the database volume is `teloa-postgres-data`, the Teloa data directory is `.runtime/teloa`, and the DSH directory is `.runtime/dsh`. The adapter log is `harness.log` inside the data directory. Redact it before sharing.

For either installation, back up both the database and the working files. Copying source code or connection settings alone is not a complete backup.

## Read-only dashboard database role

Business dashboard queries run over a dedicated login role, `teloa_business_reader`. The role can read only the table holding copies of business records and the safe helper functions, both in the schema that holds the Teloa tables (`public` by default). It has no superuser, create-database, create-role, replication or row-level-security bypass attributes, and it is not a member of any other role. Teloa checks these conditions at startup; if they do not hold, dashboard queries are disabled and the reason is written only to the server log.

The role password is generated randomly and kept in a password file inside the Teloa data directory (mode 0600, owned by the system user running Teloa); the database stores only a SCRAM verifier. To rotate the password, stop Teloa, delete the password file and start Teloa again. Startup generates a new password and updates the database role. Do not edit the file by hand or include it in shared backups.

## npm data layout

GitHub Releases provides the native `0.2.6` package; see [installation](https://docs.teloa.ai/v/0.2.6/markdown/en/start/quickstart.md) for native installation. The installer keeps program versions, application data, and working files separate: `~/.teloa/releases/` stores programs, `instances/default/` stores sessions and configuration, and `workspaces/default/` stores working files. Updating the npm package does not automatically migrate data or restart the service. Docker PostgreSQL is the default; an existing dedicated local database is an alternative. Do not mix these paths with a source or Compose installation.

## Keys and environment variables

Model keys and connection grants share the DSH key storage, which prefers encryption with a master key in the system keychain or a master key file outside the DSH folder. Auto mode falls back to plaintext only on first initialization when neither is available, with a persistent warning in Settings. An existing encrypted store is locked if its key cannot be retrieved; it does not downgrade.

- The source and container launchers drop key-like variables from the environment they start in: names that, split on `_`, contain a whole segment such as `KEY`, `TOKEN`, `SECRET`, `PASSWORD` or `AUTH`, and values that are URLs with a password (such as `postgres://user:password@…`). Only the dropped names are printed, never the values. The npm `teloa` command already passes a fixed short list of variables.
- URLs with a password in proxy variables (such as `HTTPS_PROXY=http://user:password@…`) are dropped as well, and proxy variables have no `_FILE` form; with the source or container launcher, use a local proxy forwarder that needs no password. The npm `teloa` command passes proxy variables through unchanged under its allowlist.
- Enter model keys in Settings. To inject one from a file, point `<NAME>_FILE` at a file only you can read (for example `DEEPSEEK_API_KEY_FILE`). The security action token likewise moves to `TELOA_SECURITY_ACTION_TOKEN_FILE`.
- IM channel keys (`FEISHU_APP_SECRET`, `FEISHU_ENCRYPT_KEY`, `LARK_APP_SECRET`, `LARK_ENCRYPT_KEY`, `TELEGRAM_BOT_TOKEN`, `SLACK_BOT_TOKEN`, `SLACK_APP_TOKEN`) can only be saved under **Settings > IM channels**; environment variables and `_FILE` have no effect. When the launcher drops them, the startup log shows one extra notice.
- With Teloa stopped you can maintain the key storage: `teloa credentials --action export-plaintext --confirm` writes an owner-only (`0600`) plaintext file before you roll back to the previous version; `teloa credentials --action reset --confirm` resets the store when the master key cannot be recovered and keeps the old files renamed to `.bak-<time>`. Reset deletes only the keychain entries this installation registered and never enumerates the keychain; if macOS still shows a keychain prompt, declining it does not stop the reset. The entry for the previous master key is kept so the `.bak-*` files can still be opened; delete it by hand in the system keychain (service `Teloa Credentials`) once you no longer need it.
- An npm installation needs network access to fetch the system keychain component for the current platform. If you install offline (`npm install --offline`) and the local cache lacks that component, or the platform is not supported, the program still installs, but first initialization quietly uses a master key file instead, and it never switches back to the system keychain on its own. To move to the keychain: reinstall with network access, stop Teloa, optionally run `teloa credentials --action export-plaintext --confirm` first to keep a plain-text copy for re-entry, then run `teloa credentials --action reset --confirm`, start Teloa (set `TELOA_CREDENTIALS_STORE=keyring` to require the keychain), enter your keys again in Settings, and check the current method under **Settings > Key storage**.
- `teloa backup` includes neither keys, authorizations nor the `.env` file in the DSH folder (it may hold plain-text keys). If you need it, save it yourself somewhere protected and, after a restore, put it back as `instances/default/dsh/.env` in the new installation (mode `0600`); the backup and restore commands both print this reminder.
- Security-related `TELOA_*` switches (the OAuth callback address, the security action address and token, usage statistics and online market switches, the data folder, acceptance switches and so on) are read only from the environment Teloa inherits at launch; setting them in a `.env` file in your project folder or the DSH folder has no effect. **Upgrade note:** such variables that you previously kept in either `.env` file stop working after the upgrade; Teloa prints one warning at startup listing only their names. Set them in the environment that launches Teloa instead (the npm-installed `teloa` does not pass them; put the OAuth callback in `oauth.publicCallbackUrl` in `config.json` in the data directory).
- Known forms of AI tool calls that read key storage files, master keys, the DSH folder's `.env`, database password files or backup folders are blocked. Content matching saved keys in tool output or group posts is replaced with `[已隐藏]`. This raises the bar but does not cover arbitrary encodings or every outbound path; it is not a security boundary.
- **Settings > Key storage** shows how keys are encrypted and keeps a warning on screen while the store is locked or can only save plain text. It also lists plain-text key copies left from before encryption: copies in old backups and upgrade backup folders, and `.credentials.yaml.quarantine-*` files set aside at startup. Nothing is deleted until you select the copies (one by one or all at once) and confirm again; each file is overwritten with zeros in place before it is removed. Files that share their contents with another path (hard links) are marked for manual handling in the list and cannot be selected; links, files that cannot be overwritten and files replaced after the list was built are not deleted either. SSDs, APFS and system backups (such as Time Machine) may still hold old data; overwriting with zeros does not guarantee physical erasure. Backups written elsewhere with `teloa backup --output` are not scanned; clean those up yourself.

## Skill API keys {#skill-api-keys}

Skill key configuration does not enable unsupported resources. X Search, Finance Search, Macro Data, CellCog and CellCog Image Generation remain `unsupported` and cannot be enabled by entering keys.

The following workflow applies only to a supported, installed marketplace skill that lists its required keys:

1. Open the skill under **Resources > Apps** and choose **Keys**. Review each key's purpose, required status, destination domain and path prefix, and injection location (Bearer, header or query parameter). Do not send keys in chat, write them into skill content or put them in environment variables.
2. Enter the key in the password field and save. Fields are write-only, never prefilled with stored values, and cleared on submission; the page shows only saved or missing status. Teloa saves third-party API keys in the encrypted key storage described above, using the same storage tiers and initial fallback rules. Check the actual status under **Settings > Key storage**.
3. If the catalog's key-use description changes, saving from an old page is rejected. The page refreshes the description; you must review it, enter the keys and submit again. Old input is not retried automatically. Reconfirmation clears old keys that you do not re-enter. Changes to destinations, methods, injection locations, variable names, labels and required status all require you to review again.
4. A locked or read-only store prevents saving and deletion; unknown status is not shown as missing. Resolve it under **Settings > Key storage** first. To remove keys, choose delete and confirm again: this deletes all stored keys for that skill. It does not revoke them at the third-party provider; revoke them with the provider separately.

Teloa makes the request and injects keys only for the HTTPS origins listed by the skill, path prefixes and HTTP methods. At every hop it checks that all DNS results are public addresses, locks the connection addresses and verifies the destination hostname's TLS certificate. Only same-origin redirects that stay within what the skill lists are allowed. The web search and reading master switch and domain blocklist still apply. Binding follows the currently installed, active marketplace skill; a workspace skill overriding it by name receives no injected keys. The model cannot retrieve raw values through key interfaces and does not receive these keys through environment variables.

**Limits:** the authorized third-party service receives the keys. Response bodies are redacted by the values used and common encodings; this does not cover arbitrary transformations or control a provider's echoing or forwarding, and is not a general exfiltration boundary. Skill requests follow the official DSH proxy settings (`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`). Direct connections check for public addresses and lock the connection address; through a proxy, the proxy resolves the destination, so address locking does not apply and protection comes from verifying the destination hostname's TLS certificate. Proxy addresses and accounts never appear in logs, errors or responses.

**Write confirmation and employee grants:** every request that changes data (POST, PUT, PATCH, DELETE) shows a confirmation card that you decide on the workbench; IM can only reject it. After a rejection or no answer, the same skill calling the same address with the same method does not show another card for 60 seconds. An AI employee's task session can use a skill's keys only if you tick that skill under **Employees > Capabilities**; skills added later are not granted by default, and sub-task and group-routed sessions can never use them.

**Extra headers, call guides and shared keys:** the catalog may allow a few extra request headers for a skill (up to 8); authentication, routing and forwarding headers are never allowed. The catalog may also include reviewer-written call guidance, which explains how to call the service but never widens the allowed addresses, methods or headers. Several skills can share one key: the key page lists the skills that share it, and saving or deleting applies to all of them at once.

## IM channels

IM channels (Feishu, Lark (Feishu international), Telegram, Slack) ships with the app and is loaded by default; nothing needs to be installed from the marketplace. A channel starts running as soon as you enable it in **Settings > IM channels** (no restart needed); it needs Node.js 22 or later. Teloa connects out to each platform from your machine: Telegram uses long polling, Slack uses Socket Mode, and Feishu and Lark use a long-lived event connection (to `open.feishu.cn` and `open.larksuite.com` respectively), so no public address or inbound port is needed, but the machine running Teloa must be able to reach the platform.

- Channel keys are stored only in the DSH key storage. The `im-gateway/` folder in the data directory holds channel switches, bindings and audit records (folder `0700`, files `0600`) and never contains key values or pairing codes.
- The Feishu SDK is not shipped with Teloa: the first time a Feishu or Lark channel is saved, Teloa downloads a locked version from npm, verifies its integrity, skips install scripts and places it in the data directory; both channels share this copy. Feishu and Lark channels cannot be added offline.
- Only one process per data directory may connect IM channels (`im-gateway/instance.lock`). A second Teloa shows "Another Teloa is connected to this channel" for its channels, and its settings page is read-only.
- Enabling or disabling only changes the DSH profile's bundle list; disabling keeps `im-gateway/` and the keys. Source, container and npm upgrades re-register the bundled extension at startup and check that it comes from this program's own directory, so you do not need to re-run `pnpm setup:dsh`; if the source does not match, Teloa refuses to start. If you had configured channels before upgrading, IM channels is enabled once automatically (recorded in `teloa-官方扩展.json` in the profile directory).
- `teloa backup` does not include keys: after a restore, save the channel keys again in Settings; bindings are kept in `im-gateway/`.
- When moving Teloa, keep the bindings in `im-gateway/` and re-enter keys in Settings on the new installation. Do not copy ciphertext as a substitute for re-entering them: its master key belongs to the original installation.

See [Chat with employees from IM](https://docs.teloa.ai/v/0.2.6/markdown/en/guides/im-channels.md) for usage.

## OAuth callback address

App connections on **Apps** that need OAuth authorization with the service (such as Supabase remote, GitLab and Slack) send the authorization result back to Teloa's `/oauth/callback`:

- Local deployments (Teloa listens only on `127.0.0.1`) need no configuration; the callback is `http://127.0.0.1:<port>/oauth/callback`.
- Inside the Compose container Teloa always listens on `127.0.0.1:3100`, so the callback and the authorization entry are fixed at `http://127.0.0.1:3100/oauth/…`. If you change `TELOA_PORT` to another port, the browser is sent back to 3100 after authorization and never reaches Teloa. When you use OAuth connections, keep `TELOA_PORT=3100`, or set `oauth.publicCallbackUrl` below behind an HTTPS reverse proxy so the callback matches the address you actually use.
- Server deployments (Teloa listens on `0.0.0.0` or another external address) must set a public HTTPS callback in `config.json` in the data directory. Otherwise starting authorization is refused with "Server deployments must set oauth.publicCallbackUrl in the configuration file to use OAuth authorization."

```json
{
  "oauth": {
    "publicCallbackUrl": "https://teloa.example.com/oauth/callback"
  }
}
```

- The address must use `https://` and must not contain a user name, password or `#` fragment. Your reverse proxy must forward that path unchanged to Teloa's `/oauth/callback`, and forward the sibling `start` path (such as `https://teloa.example.com/oauth/start`) to Teloa's `/oauth/start`. Authorization opens through that entry: it first writes a one-time cookie to the browser and then redirects to the authorization server, so the callback must come back to the same browser. The authorization entry link is returned only to the person who started authorization; do not forward it. The first browser to open it is bound, and other browsers cannot complete authorization.
- `config.json` lives in the data directory (`.runtime/teloa/config.json` when running from source, `/data/teloa/config.json` in Compose). It must be owned by the system user running Teloa and must not be writable by other users; otherwise the setting is ignored and a warning is logged.
- Teloa reads it once at startup; restart after changing it. Only when the file has no such setting does Teloa fall back to the `TELOA_OAUTH_PUBLIC_CALLBACK_URL` environment variable.
- The npm-installed `teloa` command passes only a fixed set of environment variables to Teloa, so `TELOA_OAUTH_PUBLIC_CALLBACK_URL` has no effect there. Set `oauth.publicCallbackUrl` in `config.json` in its data directory instead (by default `~/.teloa/instances/default/runtime/config.json`).
- Authorization tokens are also kept in the key storage and are no longer written to separate plaintext files under `mcp/credentials/`. Encryption and first-initialization fallback follow the rules above. Token values do not appear in connection configuration, pages or logs.

## After changing configuration

Check that the change was saved, that it applies to the intended employee or task, and that an actual call succeeds. Extension code or composition changes may require a build and service restart in development. Refreshing the browser does not reload server code.

See [Commands and configuration](https://docs.teloa.ai/v/0.2.6/markdown/en/reference/configuration.md) for environment variables and [Backup, restore and upgrades](https://docs.teloa.ai/v/0.2.6/markdown/en/deploy/backup.md) for maintenance.
