Files
Octop/docs/configuration.md
42e41ed33e feat: add one-shot tasks and split personalization permissions (#2157)
Scheduled jobs can run once and then stop, personalization tabs can be granted separately, and rotated logs use a date-before-extension name.

Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 07:11:24 +08:00

293 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Configuration
Octop stores all of its state under `~/.octop/`. The directory is created
on first server start (or by `octop init` / `octop run`).
## Filesystem layout
```
~/.octop/
├── config.json # process-level settings (host, port, CORS, DB, TLS, …)
├── env # optional dotenv (OCTOP_DATABASE_*, API keys, …); loaded at server start
├── octop.db # SQLite — users, agents, providers, sessions, audit
├── cli_state.json # CLI token + pinned defaults (`octop user login`)
├── repl_history # readline-style history for `octop chats repl`
├── secrets/
│ └── jwt_secret # 32-byte random; rotate with `octop admin rotate-jwt-secret`
├── agents/<agent_id>/ # per-agent workspace (LangGraph state, attachments, …)
├── plugins/ # installed third-party plugins
├── ssl/ # self-signed certs when `octop run --ssl` is used without --cert/--key
├── logs/ # rotating log files (when `octop service start` writes a logfile)
└── octop.log # default foreground log path
```
`env` is dotenv format, applied before `config.json` is loaded
(`OctopServer.start` → `apply_env_file`). Dashboard **Advanced → Environment
variables** writes this file. Saving **replaces** the file and drops removed
keys from the process environment (host/systemd keys that were never in the
file are left alone). Every running agent inherits those keys:
- **Local shell** — live process env (including this file) plus the agent's
workspace `.env` (workspace wins on the same key; `OCTOP_*` / `HOME` / `USER`
cannot be overridden from `.env`). New agents store that file at
`{workspace}/.octop/.env`; existing agents keep `{workspace}/.env` when
their stored internal system-files prefix uses the legacy layout. Remote
object-store workspaces are not read from the host path — Octop/harness
also reads `.env` through the agent backend on each execute. Octop also
injects `OCTOP_AGENT_ID`, `OCTOP_AUTH_DIR` (under `{workspace}/.octop/auth`
for new agents; legacy agents keep `{workspace}/.octop-auth`),
and `OCTOP_HOME` into shell/sandbox env.
- **Docker sandbox** — re-reads `~/.octop/env` on execute, plus workspace `.env`
and a minimal `PATH` (not the full host environment). Admin `PATH`/`HOME`
cannot override the container toolchain; workspace `.env` may set `PATH`.
- **Web search tools** (Tavily, Brave, …) register at agent start from process
env. Put search API keys in the global file, not only in an agent's `.env`.
Changing search keys triggers a background agent reload; other keys take
effect on the next execute without reload.
Per-agent secrets belong in `{workspace}/.env` or, for new agents,
`{workspace}/.octop/.env` (also writable via the `write_env_file` tool).
That file is excluded from published-expert snapshots.
The root can be overridden with `OCTOP_HOME` (absolute path). Most
sub-paths are exposed as properties on `PathLayout` in
`octop.infra.utils.paths`.
## Local knowledge indexing and systemd resource limits
Local ONNX embeddings process one chunk at a time with two inference threads.
Indexing, retrieval, and model probes share one cached model per process and
serialize inference. Switching the model or `OCTOP_HOME`, or deleting the cached
model, releases the resident session. Knowledge indexing processes one document
at a time; large imports continue in the background.
On Linux, `octop service start` and `octop service restart` install/update
`octop.service.d/15-resource-limits.conf` alongside the existing service unit.
The main unit, custom `ExecStart`, service user, and other drop-ins are preserved.
Defaults are `MemoryHigh=60%` and `MemoryMax=75%` of physical RAM,
`MemorySwapMax=512M`, `OOMPolicy=kill`, a 30-second restart delay, and at most three
starts within ten minutes. These limits cover the whole service and its children;
they are a host safeguard, not a guarantee that every model fits. Memory controls
require the corresponding systemd/cgroup support (user services need cgroup v2
with the memory controller available).
To set a machine-specific budget, use `systemctl --user edit octop.service`
(or `sudo systemctl edit octop.service` for a system service). For example:
```ini
[Service]
MemoryHigh=5G
MemoryMax=6G
MemorySwapMax=512M
```
The resulting `override.conf` sorts after the managed defaults and survives
future service updates. Restart the service to apply it. If repeated failures
hit the start limit, correct the cause before running
`systemctl --user reset-failed octop.service` and starting the service again
(omit `--user` for a system service). Foreground, Docker, and macOS launchd
deployments retain their own process/container resource policies.
## `config.json`
Generated with defaults on first run; merged with environment overrides
on each start. Schema (`OctopConfig` in `octop/config.py`):
```json
{
"bind_host": "127.0.0.1",
"port": 8088,
"log_level": "info",
"access_token_ttl_seconds": 86400,
"login_max_attempts": 5,
"login_lockout_seconds": 900,
"cors_origins": [],
"default_timezone": "Asia/Shanghai",
"enable_dashboard": true,
"enable_api_docs": false,
"require_setup_password": true,
"max_upload_mb": 100,
"database": {
"driver": "sqlite",
"sqlite_path": "octop.db",
"host": "127.0.0.1",
"port": 5432,
"database": "octop",
"user": "octop",
"password": null,
"url": null
},
"tls": {
"enabled": false,
"mode": "",
"domains": [],
"cert_file": "",
"key_file": "",
"issued_at": "",
"expires_at": "",
"acme_staging": false,
"http_port": 80
}
}
```
Notes:
- `default_timezone` is the process default for dashboard timestamps, cron
scheduling, and harness. Legacy `cron_timezone` in `config.json` and
`OCTOP_CRON_TIMEZONE` are still accepted; the new key/env wins when both
are set.
- `database.password`: when the wizard configures PostgreSQL via discrete
fields, this **may** be written into `config.json` (convenient for local
first-run). In production, prefer using only `OCTOP_DATABASE_PASSWORD` or
a `OCTOP_DATABASE_URL` that includes the password, and restrict
`config.json` file permissions. Environment variables always override the
same-named setting in the file.
- `enable_api_docs=false` keeps `/api/docs` (Scalar) off in production
while still serving `/api/openapi.json` to the dashboard.
- `require_setup_password=true` adds the wizard password gate to the
first-run setup flow; set `false` for unattended bootstraps via
`OCTOP_ADMIN_USERNAME` / `OCTOP_ADMIN_PASSWORD`.
- `max_upload_mb` is the process-wide ceiling for dashboard chat
attachments, IM inbound files, and knowledge-base documents (default
100). Existing `config.json` files that omit the key pick up the
default on load. Change it and restart; reverse proxies may still
impose their own body-size limit (for example nginx
`client_max_body_size`). Agent workspace file upload, plugin ZIPs,
and backup archives use separate limits and are not this setting.
- `plugins.<id>.enabled` is the **global** plugin switch (Dashboard Admin →
Plugins). Bundled plugins are copied into `~/.octop/plugins/` on init and
server start with `enabled: false`. `bundled_plugins_seeded` lists ids
already offered so uninstall does not come back on the next start.
## Environment overrides
Each variable, when set, takes precedence over the matching key in
`config.json`. Unset variables leave the on-disk value untouched.
| Variable | Type | Default | Effect |
|----------|------|---------|--------|
| `OCTOP_HOME` | path | `~/.octop` | Install root (DB, secrets, workspaces, plugins) |
| `OCTOP_BIND_HOST` | string | `127.0.0.1` | Listen address (use `0.0.0.0` for LAN access) |
| `OCTOP_PORT` | int | `8088` | Listen port (`0`–`65535`; `0` asks the OS for a free port) |
| `OCTOP_LOG_LEVEL` | string | `info` | One of `debug` `info` `warning` `error` |
| `OCTOP_LOG_RETENTION_DAYS` | int | `14` | Keep rotated `octop.YYYY-MM-DD.log` files for this many days |
| `OCTOP_LOG_MAX_BYTES` | int | `104857600` (100 MiB) | Roll the active log when it exceeds this size (in addition to daily rotation) |
| `OCTOP_LOG_COMPRESS` | bool | `true` | logrotate-style `compress` + `delaycompress`: gzip prior rotated plains on the next cycle (`octop.YYYY-MM-DD.log.gz`); the newest rotated file stays uncompressed until then |
| `OCTOP_ACCESS_TOKEN_TTL` | int (seconds) | `86400` | JWT access-token lifetime |
| `OCTOP_LOGIN_MAX_ATTEMPTS` | int | `5` | Failed-login attempts before lockout |
| `OCTOP_LOGIN_LOCKOUT_SECONDS` | int | `900` | Lockout duration after `OCTOP_LOGIN_MAX_ATTEMPTS` failures |
| `OCTOP_CAPTCHA_PROVIDER` | slug | `slider` | Login captcha (`slider`, `turnstile`, `hcaptcha`, `recaptcha-v3`, `tencent`, `geetest-v4`; `recaptcha` v2 stays resolvable for existing configs but is unlisted). Boot snapshot; restart after change |
| `OCTOP_CAPTCHA_SITE_KEY` | string | empty | Public site key (Tencent: CaptchaAppId; required when the env snapshot is a strong provider) |
| `OCTOP_CAPTCHA_SECRET` | string | empty | Siteverify secret (Tencent: AppSecretKey; never logged; `GET /api/envs` redacts it) |
| `OCTOP_CAPTCHA_CAM_SECRET_ID` | string | empty | Tencent only: CAM API SecretId signing `DescribeCaptchaResult`; required when the env snapshot is `tencent` |
| `OCTOP_CAPTCHA_CAM_SECRET_KEY` | string | empty | Tencent only: CAM API SecretKey; never logged; `GET /api/envs` redacts it |
| `OCTOP_CAPTCHA_V3_MIN_SCORE` | float | `0.5` | Minimum `recaptcha-v3` score; admin UI is read-only |
| `OCTOP_DEFAULT_TIMEZONE` | IANA tz | `Asia/Shanghai` | Default timezone for display, scheduling, and harness (`cron_timezone` / `OCTOP_CRON_TIMEZONE` still accepted) |
| `OCTOP_CORS_ORIGINS` | comma-sep list | empty | Permitted CORS origins for the dashboard / external callers |
| `OCTOP_ENABLE_DASHBOARD` | bool | `true` | Serve the built React SPA at `/` |
| `OCTOP_ENABLE_API_DOCS` | bool | `false` | Expose Scalar API docs at `/api/docs` |
| `OCTOP_REQUIRE_SETUP_PASSWORD` | bool | `true` | Require wizard password during initial setup |
| `OCTOP_MAX_UPLOAD_MB` | int | `100` | Max upload size in MiB for chat attachments, IM inbound, and knowledge documents (1–1024) |
| `OCTOP_DATABASE_URL` | string | empty | Full DSN — overrides the `OCTOP_DATABASE_*` fields below |
| `OCTOP_DATABASE_DRIVER` | `sqlite` \| `postgresql` | `sqlite` | Storage backend |
| `OCTOP_DATABASE_SQLITE_PATH` | path | `octop.db` | SQLite file path (relative to `OCTOP_HOME` unless absolute) |
| `OCTOP_DATABASE_HOST` | string | `127.0.0.1` | PostgreSQL host (when `driver=postgresql`) |
| `OCTOP_DATABASE_PORT` | int | `5432` | PostgreSQL port |
| `OCTOP_DATABASE_NAME` | string | `octop` | PostgreSQL database name |
| `OCTOP_DATABASE_USER` | string | `octop` | PostgreSQL user |
| `OCTOP_DATABASE_PASSWORD` | string | empty | PostgreSQL password (overrides file; prefer env in production) |
| `OCTOP_ADMIN_USERNAME` | string | empty | Pre-fills the first-admin username in `octop init` |
| `OCTOP_ADMIN_PASSWORD` | string | empty | Pre-fills the first-admin password in `octop init` |
| `OCTOP_ADMIN_DISPLAY_NAME` | string | empty | Pre-fills the admin display name |
| `OCTOP_USER` | string | empty | Default `--user` for CLI subcommands |
| `OCTOP_AGENT` | string | empty | Default `--agent` for CLI subcommands |
| `OCTOP_SERVICE_MODE` | `systemd` \| `launchd` | auto | Override the service backend (used by `octop service`) |
| `OCTOP_SERVICE_SCOPE` | `user` \| `system` | auto | systemd `--user` vs system unit (Linux only) |
Invalid integer values are logged and ignored — the on-disk default
remains in effect. `database_env_configured()` returns `True` when any
`OCTOP_DATABASE_*` is set, which lets `OctopServer.start()` pick the
configured backend at boot.
**Docker Compose:** put `OCTOP_DATABASE_*` in `docker/.env` *and* ensure
they are listed under `environment:` in `docker/docker-compose.yml`
(Compose uses `.env` for interpolation only; unset keys do not enter the
container). Writing the same keys to the mounted `~/.octop/env` also works.
### Agent memory vs control plane
Control-plane `database` and agent memory are separate layers. Defaults:
- Control plane SQLite → agent memory stays `{workspace}/memory.sqlite`
(or `{workspace}/.octop/memory.sqlite` for new agents).
- Control plane PostgreSQL → agent memory **defaults to the same DSN**
(octop-memory per-agent PG schema `agent_<id>`). Runtime also needs
``langgraph-checkpoint-postgres`` (pulled in via
``octop-memory[langgraph-postgres]``) so LangGraph checkpoints work.
To keep file memory while the control plane is PG, set on the agent:
```json
"memory": { "backend": { "type": "sqlite" } }
```
Or point at another DSN with `"type": "postgres", "dsn": "…"`. There is
no automatic SQLite→PG memory data migration.
PostgreSQL **extensions** (e.g. `vector`) are instance-level ops, not
control-plane migrations. Local compose enables `vector` via
`docker/postgres/init-vector.sql`; managed databases need a DBA /
provider toggle. See [ADR 002](./adr/002-database-backends.md).
## First-boot wizard
The first request to a fresh install lands on the setup page. Greenfield
SQLite installs **defer** opening the control-plane DB until the database
step; password verification works without a pool. The modern flow uses
`/api/setup/*`:
1. `POST /api/setup/begin` (or `POST /api/setup/verify-password` when
`require_setup_password=true`) — issues a short-lived wizard token.
2. `POST /api/setup/test-database` / `POST /api/setup/database` — choose
SQLite or PostgreSQL, probe, persist `config.json`, bind pool + migrate.
3. `POST /api/setup/initial-admin` — creates the seed admin (requires DB).
4. `POST /api/setup/test-provider` — pings an optional provider draft.
5. `POST /api/setup/finish` — bootstraps default `main` agent and unlocks
the rest of the API.
`GET /api/setup/status` returns `setup_required`, wizard password fields,
plus `database_bound` / `database_driver`. `setup_lockdown` middleware
blocks non-setup routes until the wizard completes.
For unattended installs, use `octop init --yes` with
`OCTOP_ADMIN_USERNAME` / `OCTOP_ADMIN_PASSWORD` (and
`OCTOP_REQUIRE_SETUP_PASSWORD=false` if the env-var path is used). This
runs the same migrations + admin creation without the HTTP wizard.
## Secrets
The JWT secret is generated on first start and stored in
`~/.octop/secrets/jwt_secret`. Rotate it with:
```bash
octop admin rotate-jwt-secret
```
Rotation invalidates every outstanding access token immediately. The
old secret is overwritten in place — no zero-downtime rotation today.
Per-agent provider credentials (e.g. API keys) live in the SQLite
`providers` table and are surfaced through
`infra/connectors/credential_crypto.py` for connector OAuth flows.
## Optional segmented history archive
`history_v2_enabled` defaults to `false`; enable it with
`OCTOP_HISTORY_V2_ENABLED=true`.
This switch only affects the write format of the next complete turn —
once disabled, turns already saved in the new format are still read
correctly.
Before enabling, read
[Segmented history archive & rollback](versioned-history.md), especially
the version compatibility and backup/restore boundaries it covers.