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>
Apply the remaining working-tree changes: connectors empty-state layout,
plus the knowledge, HITL, captcha, workspace, user-cache, and SSRF
adjustments with matching tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds 极验行为验 v4 to the login captcha layer, following the provider
plug-in seam (one dataclass + register call backend-side, one widget
adapter frontend-side; the settings UI is data-driven so only locale
keys were needed):
- providers.py: _GeetestV4Provider — login token carries the frontend
getValidate() result as JSON (lot_number / captcha_output /
pass_token / gen_time); siteverify posts form-urlencoded to
gcaptcha4.geetest.com/validate?captcha_id=<id> with
sign_token = HMAC-SHA256(captcha_key, lot_number); only
result == "success" passes (fail / status:error both reject).
Aliases: geetest, geetest4, gt4. Fail-closed on transport errors,
consistent with the other providers.
- CaptchaField: geetest-v4 popup via gt4.js bind mode — initGeetest4,
showCaptcha on ready, getValidate() serialized as the captcha_token;
error/close resolve undefined so login re-prompts.
- Settings UI: captcha_id / captcha_key fields via locale keys.
Tests: provider unit tests (URL + form fields + HMAC against an
independent computation, malformed/missing-field rejection, interpret
matrix), ensure_captcha flow through a local siteverify double (form
body + signature asserted; fail result rejects), popup adapter tests
(validate payload JSON, error path), builtin-order + integration list
assertions updated. Locale parity green.
The config.json example in docs/configuration.md omitted the password
and url fields from DatabaseConfig, even though both are documented
in the surrounding text and present in src/octop/config.py. Add them so
the snippet matches the actual schema.
docs/configuration.md is an English-only reference doc, but two chunks
were left untranslated: the database.password bullet note, and the
entire 'Optional segmented history archive' subsection. Translated
both to English so the doc reads consistently for English readers.
* feat(auth): add optional login captcha (slider, turnstile/hcaptcha/recaptcha, tencent)
Password login can now be guarded by a captcha. Default slider stays
client-only; strong providers are verified server-side with the vendor
(Cloudflare Turnstile, hCaptcha, reCAPTCHA v2/v3, Tencent Cloud Captcha).
- infra/auth/captcha: env snapshot + settings blob resolution, provider
registry with per-vendor verify calls, siteverify with 10s timeout
- GET /api/auth/captcha (public widget config); POST /api/auth/login
accepts captcha_token; tencent token carries ticket:randstr and is
checked via GET with the client IP
- admin GET/PUT /api/settings/captcha behind the new captcha permission;
OCTOP_CAPTCHA_SECRET redacted in envs API; boot validation for strong
env providers
- dashboard: CaptchaField with slider/checkbox/invisible/popup modes,
login page wiring, advanced-settings captcha panel
- docs + i18n (en/zh) + unit/integration/frontend tests
* fix(captcha): verify Tencent tickets via DescribeCaptchaResult (TC3)
The legacy ssl.captcha.qq.com/ticket/verify endpoint rejects current
tickets with response=15 "decrypt fail" even for correct
CaptchaAppId/AppSecretKey pairs. Switch to the official ticket-check API:
- POST captcha.tencentcloudapi.com DescribeCaptchaResult (2019-07-22),
TC3-HMAC-SHA256 signed with CAM API keys; AppSecretKey stays in the body
- captcha settings gain cam_secret_id/cam_secret (encrypted at rest,
env fallback OCTOP_CAPTCHA_CAM_SECRET_ID/KEY); settings view exposes
cam_secret_id + has_cam_secret; dashboard shows the two fields for the
tencent provider only
- interpret maps CaptchaCode (1 OK; 7/8/9/15/16/21/100 fail) and
EvilLevel=100 (malicious) instead of the legacy response=="1"
- TC3 signer moved voice/tencent_sign.py -> utils/tencent_sign.py (shared)
- drop the redundant Host header from signed requests: httpx sets Host
from the URL (same value the signature covers), and an explicit Host
let host-routing system proxies intercept localhost mock calls
- secrets no longer appear in httpx URL logs (payload is in the body)
- move the Login Captcha settings tab next to HTTPS
* polish(dashboard): show reCAPTCHA v3 min score only when that provider is active
* fix(dashboard): captcha widgets follow the site locale, not the browser
- tencent popup: userLanguage from i18n.language (was navigator.language)
- turnstile: pass language render option
- hcaptcha/recaptcha/recaptcha-v3: hl script param from UI locale
- slider already renders site-provided labels
* feat(captcha): unlist reCAPTCHA v2 from the settings catalog
No verified deployment key for v2; keep the provider registered so
existing configs still verify, but stop offering it in the dropdown
(list_providers now filters on a per-provider listed flag).
* feat(cli): octop captcha reset — offline lockout escape hatch
Clears the stored captcha settings blob so login falls back to the
built-in slider when a misconfigured provider blocks all logins
(settings UI requires login, so it cannot fix itself). Boot-env
captcha is untouched; a running server needs a restart.
CHANGELOG: cover reset command, TC3 switch, widget language, v2 unlist.
* feat(cli): register octop captcha reset command and add changelog
Without the registry entry the command module from c5a7649d was
unreachable; CHANGELOG covers the login captcha feature, the TC3
ticket-check switch, widget locale, v2 unlisting, and captcha reset.
* fix(captcha): close pre-merge gaps + document the provider architecture
- envs API: redact OCTOP_CAPTCHA_CAM_SECRET_KEY like OCTOP_CAPTCHA_SECRET
(was exposed in plaintext by GET /api/envs)
- validate_boot: env-only tencent provider now requires the CAM keys,
failing fast at boot instead of at first login
- docs/configuration.md: document the two CAM env vars; note recaptcha
v2 is unlisted but resolvable
- providers.py: architecture docstring (four layers + settled design
decisions + adding-a-vendor recipe); rename _SuccessProvider to
_FormPostProvider; drop the register() cast, which exposed a real
structural bug: frozen-dataclass providers never satisfied the
Protocol's mutable metadata members — declare them read-only
properties so the structural check is real
- verify.py: docstring naming it the execution layer
Prevent multi-GB daily octop.log growth with size-based rotation, suppress noisy memory maintenance INFO, and gzip prior rotated files on the next cycle (Python gzip works on Windows).
* feat: workspace backend sandboxing, execute-env injection, and ephemeral image refs
Workspace file-I/O hardening across backend and dashboard:
- Host directory sandboxing via root_dir allow-list and denied prefixes
- Execute-environment defaults injected into harness backend specs
- Ephemeral workspace image rematerialization for model calls
- Attachment hint expansion and inbound store improvements
- Portable memory backend and skill workspace catalog updates
- Dashboard UI for agent backend fields and root-dir selection
- Bump harness-agent 0.9.23 / harness-memory 0.9.7; docs + install notes
* feat: add user invitation codes with admin management and redeem flow
- Add user_invites table (migration 009) plus SQLite/Postgres reconcile paths
- Add InviteRepo and InviteService for create/revoke/list/redeem
- Expose public validate/redeem endpoints and admin create/revoke/list routers
- Extract default-agent bootstrap into octop.infra.agents.default_agent
- Add invite error codes and en/zh i18n strings
- Dashboard: invite drawer, login redeem UI, users list, and locales
* fix: add Windows cross-platform guards for pre-existing sandbox/rootfs tests
- Guard POSIX/container-only tests (rootfs-absolute /.octop/... workspace
paths) with posix_only in test_thread_artifacts.py and test_agent_manager.py
- Normalize the rendered workspace path assertion in test_octop_builtin_skills.py
(forward slashes on Windows, not str(tmp_path) backslashes)
- Canonicalize ws_dir before the prefix check in attachment hint/path tests
(tempfile may yield an 8.3 short path on Windows while the resolved
attachment path uses the long form)
These failures predate the user-invites commit and are unrelated to it; the
product path logic is correct.
---------
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Running shells and Docker sandboxes now pick up ~/.octop/env (and workspace .env) without a full reload; MCP stdio no longer inherits the entire host environment. Usage accounting adds every model call in the turn, and workspace I/O survives the harness rebuild window.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Keep dashboard chat turns attachable after reconnect, reuse one terminal
session store across Workbench and the chat dock, and migrate legacy
hard-cut thread titles to clipped titles with ellipsis.
* feat: PostgreSQL dual-backend for control plane
Add SqlitePool|PostgresPool behind DatabasePool, parallel PG migrations,
setup wizard defer/bind, pg_dump backups with cross-engine refusal, and
memory defaulting to the control-plane DSN when using PostgreSQL.
* fix(chat): delete checkpoint data when a conversation thread is deleted
Deleting a thread only removed Octop's own metadata row; the actual
message history stayed in the agent's LangGraph checkpointer forever,
making "delete conversation" cosmetic. Also confirm before deleting a
session in the UI, since the action is now genuinely destructive.
Checkpoint deletion runs before the metadata row is removed: if it
fails outright (not just "nothing to delete"), the thread stays
visible/retryable instead of orphaning data with no remaining handle.
* fix(memory): stop suggesting pg_dump for portable export of postgres memory
The 501 hint told users to "use pg_dump on the memory schema instead".
That advice predates the shared-table layout: memory for ALL agents now
lives in one harness_memory schema isolated by a namespace column, so a
schema-level pg_dump would export every agent's memory, not just the
requester's — a cross-agent data exposure if followed on a multi-user
install.
Document the two migration models where the refusal is implemented:
sqlite memory is per-agent files moved via pack/adopt; postgres memory
is shared, and the supported way to use it from another host (e.g.
OpenClaw's harness-memory bridge with --backend postgres) is pointing
that host at the same DSN and namespace — shared, not migrated.
* fix(test): update test_slash_compact to renamed SqlitePool
The /compact test module still imported the pre-rename DBPool, which
broke collection (ImportError) on this branch after develop's compact
feature was merged in — DBPool was renamed to SqlitePool (the concrete
class) with DatabasePool now the Protocol.
DBPool -> SqlitePool at all three sites: the import, the _agent_manager
type hint, and the fixture's DBPool(path) instantiation. SqlitePool is
the byte-for-byte successor of DBPool and matches how every other test
constructs a pool; DatabasePool cannot be used at the instantiation
site (a Protocol is not instantiable).
pytest -m live now collects cleanly (31 selected, 0 errors);
test_slash_compact 3 tests pass.
* fix(test): make memory_backend db_path assertion separator-agnostic
test_explicit_sqlite_overrides_postgresql_control_plane compared the
resolved db_path against the literal "/tmp/ws/memory.sqlite", which the
code never produces on Windows: it builds the path with pathlib, so the
Windows CI job got "\tmp\ws\memory.sqlite" and the string equality failed.
Compare as Path objects so the assertion holds on every OS separator,
matching the .as_posix() discipline already used in test_config.py.
Restore syncs providers and reloads agents in-process; rename cron_timezone to
default_timezone with GET /api/settings/timezone; reload only impacted agents
after provider changes; align dashboard timestamps to the server timezone.
Co-authored-by: jubaoliang <jubaoliang@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>