* fix(config): 绑定端口越界时告警回落,--port 越界直接报错 OCTOP_PORT 只按 int() 解析、octop run --port 也只有 type=int,70000/-1 这类根本无法绑定的取值会盖掉 config.json 里本来可用的端口,进程死在 socket.bind 抛出的 OverflowError 里,报错既不含变量名也看不出端口来源; --port 更是在启动之前就把该值写进 config.json,之后每次 octop run 都继续继承。 现按 resolve_bind 已经支持的「0 表示由系统随机分配端口」把取值收口到 0-65535:环境变量越界沿用「告警 + 不覆盖」的既有契约回落配置文件端口, 命令行 --port 越界交给 click.IntRange 在使用期错误里拦下、不落盘。 * docs: 补上绑定端口的取值范围说明 `octop run --port` 现在会渲染成 `INTEGER RANGE … [0<=x<=65535]`,环境变量表也顺手写明 `OCTOP_PORT` 接受 0–65535(0 = 由系统随机分配),与 `OCTOP_LOG_LEVEL` 那行「One of …」的写法一致。
13 KiB
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/USERcannot be overridden from.env). New agents store that file at{workspace}/.octop/.env; existing agents keep{workspace}/.envwhen 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.envthrough the agent backend on each execute. Octop also injectsOCTOP_AGENT_ID,OCTOP_AUTH_DIR(under{workspace}/.octop/authfor new agents; legacy agents keep{workspace}/.octop-auth), andOCTOP_HOMEinto shell/sandbox env. - Docker sandbox — re-reads
~/.octop/envon execute, plus workspace.envand a minimalPATH(not the full host environment). AdminPATH/HOMEcannot override the container toolchain; workspace.envmay setPATH. - 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.
config.json
Generated with defaults on first run; merged with environment overrides
on each start. Schema (OctopConfig in octop/config.py):
{
"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_timezoneis the process default for dashboard timestamps, cron scheduling, and harness. Legacycron_timezoneinconfig.jsonandOCTOP_CRON_TIMEZONEare 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 intoconfig.json(convenient for local first-run). In production, prefer using onlyOCTOP_DATABASE_PASSWORDor aOCTOP_DATABASE_URLthat includes the password, and restrictconfig.jsonfile permissions. Environment variables always override the same-named setting in the file.enable_api_docs=falsekeeps/api/docs(Scalar) off in production while still serving/api/openapi.jsonto the dashboard.require_setup_password=trueadds the wizard password gate to the first-run setup flow; setfalsefor unattended bootstraps viaOCTOP_ADMIN_USERNAME/OCTOP_ADMIN_PASSWORD.max_upload_mbis the process-wide ceiling for dashboard chat attachments, IM inbound files, and knowledge-base documents (default 100). Existingconfig.jsonfiles 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 nginxclient_max_body_size). Agent workspace file upload, plugin ZIPs, and backup archives use separate limits and are not this setting.plugins.<id>.enabledis the global plugin switch (Dashboard Admin → Plugins). Bundled plugins are copied into~/.octop/plugins/on init and server start withenabled: false.bundled_plugins_seededlists 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.log.YYYY-MM-DD 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.log.YYYY-MM-DD.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.sqlitefor new agents). - Control plane PostgreSQL → agent memory defaults to the same DSN
(octop-memory per-agent PG schema
agent_<id>). Runtime also needslanggraph-checkpoint-postgres(pulled in viaoctop-memory[langgraph-postgres]) so LangGraph checkpoints work. To keep file memory while the control plane is PG, set on the agent:
"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.
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/*:
POST /api/setup/begin(orPOST /api/setup/verify-passwordwhenrequire_setup_password=true) — issues a short-lived wizard token.POST /api/setup/test-database/POST /api/setup/database— choose SQLite or PostgreSQL, probe, persistconfig.json, bind pool + migrate.POST /api/setup/initial-admin— creates the seed admin (requires DB).POST /api/setup/test-provider— pings an optional provider draft.POST /api/setup/finish— bootstraps defaultmainagent 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:
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, especially
the version compatibility and backup/restore boundaries it covers.