Scenario
Before a bot serves real users, an operator faces three questions: who changed the provider key yesterday and deleted that pipeline; who can log in, and is there a second door besides the password; and which signature is on the installed plugin archive and which code is running.
On 4.10 these were answered by grepping container logs and trusting one set of credentials. 4.11 adds the accountability layer: workspace-scoped operation logs, two-factor authentication (2FA) plus passkeys, and plugin certification with plugin signatures.
How it works in 4.11
Operation logs. 4.11 adds the workspace-scoped operation log table workspace_operation_logs (source LangBot/src/langbot/pkg/persistence/alembic/versions/0033_operation_traceability.py). Each row carries the workspace, actor name/role, auth type, route, action, resource type/id, outcome and status code, a changes before/after diff, dedupe_key, client IP and duration (entity LangBot/src/langbot/pkg/entity/persistence/operation_log.py:47).
Levels are off / mutation / read and default to off (LangBot/src/langbot/pkg/operation_trace/service.py:92). Each row's record_hash is an HMAC-SHA256 value chained to the previous row's prev_hash (service.py:200), so the panel can show "verified" vs "possibly tampered" and filter for hash mismatches or broken chains.
This audit surface is separate from the Runner's own event_log (entity LangBot/src/langbot/pkg/entity/persistence/event_log.py); the two are deliberately kept apart — event_log is not the source of this page.
2FA and passkeys. The account panel supports TOTP and WebAuthn passkeys. The TOTP secret is never stored in the clear: a Fernet token whose wrapping key is derived from the instance JWT secret via HKDF-SHA256 (LangBot/src/langbot/pkg/api/http/service/totp.py, salt langbot.totp.secret.v1); recovery codes are only PBKDF2-HMAC-SHA256 digests (600000 iterations). The login challenge lasts 5 minutes with at most 5 attempts, and the login route separates "second factor required" (totp_required) from "bad code" (totp_invalid_code) (LangBot/src/langbot/pkg/api/http/controller/groups/user.py:118-141). Passkeys use WebAuthn registration and authentication, with credentials in passkey_credentials (migration 0024_passkey_credentials.py). Workspace roles are fixed (owner / admin / developer / operator / viewer, LangBot/src/langbot/pkg/api/http/authz.py:42-63); the last owner cannot be deleted or demoted (docs/multi-tenant/workspace-multi-user-architecture.md §5.5).
Plugin signatures and certification. On install, Core calls the SDK's langbot_plugin.certification.verify_archive() before writing the artifact: a strict certificate envelope in the ZIP comment is verified against the signed normalized ZIP digest without extracting the payload (docs/architecture/certified-plugins.md). The outcome lands in the plugin record install_info._certification (source LangBot/src/langbot/pkg/plugin/connector.py:1896-1910). Only verification: valid with both shared-runtime-v1 and stateless-v1 can reach a shared runtime profile; OSS ships with an empty trusted key ring, so unresolvable declarations fall back to dedicated. administrator_force is honored only as the literal boolean true.
Measured on 2026-10-16: a 4.10.11 database at alembic_version = 0023_bot_scoped_sessions migrated automatically to 0037_metadata_text — 14 revisions, 50 tables — with passkeys (0024), TOTP (0025), certification digest backfill (0032) and operation logs (0033) in range.
Hands-on steps
Start a local demo instance:
# 4.11.0 (fresh instance)
docker network create lang7-411
docker run -d --name lang7-411-core --network lang7-411 --restart on-failure -e TZ=Asia/Shanghai \
-e BOX__ENABLED=false -e SPACE__DISABLE_TELEMETRY=true -p 15400:5300 -p 15480-15485:2280-2285 \
-v "$PWD/deploy/411/data:/app/data" rockchin/langbot:v4.11.0
docker run -d --name lang7-411-runtime --network lang7-411 --network-alias langbot_plugin_runtime --restart on-failure \
-e TZ=Asia/Shanghai -e SPACE__DISABLE_TELEMETRY=true -v "$PWD/deploy/411/data/plugins:/app/data/plugins" \
rockchin/langbot:v4.11.0 uv run --no-sync -m langbot_plugin.cli.__init__ rt
The initial admin account is admin@langbot.local with password LangBot411!demo (a local demo value — replace it). Three places to visit:
- Operation logs: Settings → Operation logs. Set "Operation level" from off to mutation or read, adjust retention / max rows / dedupe window, and save. Then filter the record list by action, resource type and actor and expand "Details" for the change set.
- 2FA: Settings → Account → "Two-step verification". Click Enable → Generate secret, scan the QR code, enter the 6-digit code and confirm; recovery codes appear only at that confirm step (the UI copy is "Two-factor authentication is enabled · {{count}} recovery codes remaining"). The same panel offers "Add passkey". Owners and admins can also view and reset other accounts' 2FA.
-
Plugin certification: marketplace versions with the "certified plugin" badge (gated on the marketplace-side
certificate_status: "issued") are labelled as reviewed for data isolation and shared-runtime compatibility, with a signed archive. The instance-side admission fields —admission_code/verification/certificate_id/runtime_profile— have no WebUI surface at all (a search ofweb/srcreturns zero hits — the market badge is the only render), so read them frominstall_info._certificationinGET /api/v1/plugins.
Verify
-
Operation logs: after raising the level to mutation, make one config change and
GET /api/v1/settings/operation-logsshould return that row. That route, plus.../filtersand.../export, requiresaudit.view(sourceLangBot/src/langbot/pkg/operation_trace/routes.py:15); paging caps at 200 rows and export at 10000 (service.py:109,service.py:2293). -
2FA: after enrolling, log in again and you get the two-step challenge; a wrong code returns
totp_invalid_code, and a recovery code takes the same path. Enrolment was not completed in this measurement, so this bullet comes from the UI copy and the source, not an observed result:GET /api/v1/user/totp/statusstayed{"enabled": false, "pending": false, "confirmed_at": null, "recovery_codes_remaining": 0}throughout. -
Plugin certification: the measured plugin came from the marketplace, and the two sides report its version differently (recorded 2026-10-16):
- instance side
install_info._certification(verbatim):{"admission_code": "CERTIFIED_PLUGIN_OSS_UNTRUSTED_DEDICATED", "artifact_digest": "2d2ce17055d70dd7736e6902595be3262992fd1598a00bd9d79d19fef1c16329", "certificate_component_model": "stateless-v1", "certificate_id": null, "certificate_runtime_profile": "shared-runtime-v1", "normalized_digest": "fc70e08968d8358a33e2cb3032bd1846b8acf38b40b4a7b5f66032c2e12e8205", "runtime_profile": "dedicated", "verification": "invalid"} - marketplace side, same version: the plugin record (
GET https://space.langbot.app/api/v1/marketplace/plugins/langbot-team/LocalAgent) reportscertification_status: "issued"andcertificate_id: cert:86f25832a3f5df65b6e2f938155f3a855ed69ead87d835ddf54c49b4607b629f; the version record (same path plus/versions) addscertificate_key_id: ed25519:86e697952a6c3bc4. - How to read it:
verificationis the verdict on this instance,runtime_profilethe profile actually selected, and anullcertificate_idmeans no certificate ID was attached. Cross-checking the two sides is your job, and anissuedmarketplace badge must not be read as proof of Cloud admission.
- instance side
Limits and caveats
- Operation logs are off by default (
DEFAULT_OPERATION_LEVEL = 0): nothing is recorded until you enable them; the store is append-only with retention as the only deletion path. - Operation logs cover admin actions inside the LangBot instance only — not the external platform side (WeChat, Lark, DingTalk and so on). Where a platform example appears: Support depends on the adapter and platform — check the support matrix first.
- Roles are fixed RBAC: developer, operator and viewer never get
audit.view, custom roles are not supported, and OSS is one workspace with multiple accounts — the architecture document still labels itself a baseline, not a ready-to-ship multi-tenant deployment. - Certification is an admission decision, not execution isolation. Runner plugins are operator-owned execution: LangBot protects its own resources with run-scoped authorization and a scoped MCP gateway using a short-lived
run_token, while the execution environment is the user's or deployment platform's — no managed sandbox is provided (docs/agent-runner-pluginization/SECURITY_HARDENING.md). - These three mechanisms prepare for going live; they are not all that going live requires.
Summary and next steps
If your goal is to bring a 4.10 instance over and add this governance layer on top, start with the pinned migration document docs/pipeline-migration-config-map.zh-CN.md and the v4.11.0 release. Managed hosting is only a conditional secondary option at space.langbot.app; this article makes no promise about it.



Top comments (0)