Files
HiveGrid-Wireframes/hivegrid-cloud/docs/features/chatbot-v1.md
T

5.1 KiB

Chatbot V1 — User Allow-List, 8 Capabilities, Audit Log

Built on top of the MVP. V1 ships:

  • 8 capabilities (MVP 3 + data_read + 4 high-risk).
  • Per-user override via the new TenantChatbotAccess table.
  • Audit-event-per-high-risk-call through @toolctrl/db/audit-events.server.
  • Per-capability sliding-window rate-limits via @toolctrl/shared-store.

Capability matrix

Capability Risk Tool name Notes
qa_help low search_help Bundled docs ranker. No DB.
kb_search low search_knowledge_base Tenant-scoped KB. Wraps results in ---KB ARTICLE--- markers.
account_info low get_account_info Whitelist projection. Never returns billing tokens / customer ids.
data_read medium read_entities Strict entity enum, tenant-scoped read.
data_write high write_entity Audit + rate-limit (30/min default).
workflow_trigger high start_workflow Audit + rate-limit (10/min default).
admin_actions high admin_action Audit + rate-limit (5/min) + OWNER/ADMIN role gate.
external_tools high invoke_external_tool MCP-tool registry. Audit + rate-limit (60/min default).

Allow-list semantics

TenantChatbotAccess is a per-(tenantId, userId) override row.

  1. Master-flag chatbot OFF → no capabilities.
  2. Row exists with enabled = false → explicit deny (wins over any tenant default).
  3. Row exists with enabled = true and non-empty capabilityScopes → those scopes.
  4. Row exists with enabled = true and empty scopes → fall through to tenant default.
  5. No row, chatbotAccessMode = 'all_members' → Tenant.chatbotDefaultCapabilities.
  6. No row, chatbotAccessMode = 'allow_list' → no capabilities.

Audit pipeline

auditChatbotToolCall writes through createAuditEvent. Action format: chatbot.tool_call.<capability>.<toolName>. Payload uses an allow-list projection (ALLOWED_ARG_KEYS); anything not on the list is dropped so prompt-injection cannot exfiltrate secrets into the audit table.

Outcomes:

  • success — inner tool ran cleanly.
  • failure — inner tool threw (rethrown to the SDK).
  • blocked — rate-limit denied or role-check returned forbidden.

Rate-limit defaults (sliding window, 60 s)

Capability Max / window
qa_help 1000
kb_search 1000
account_info 1000
data_read 200
data_write 30
workflow_trigger 10
admin_actions 5
external_tools 60

Bucket key: chatbot:<capability>:<tenantId>:<userId|anon>. Backed by the shared-store (Redis in prod, MemoryAdapter in tests).

Operator commands

# Toggle the master flag
toolctrl chatbot enable | disable | status

# Grant / revoke per-user access
toolctrl chatbot grant --tenant acme --email alice@example.com --capabilities=qa_help,kb_search,data_read
toolctrl chatbot revoke --tenant acme --email bob@example.com        # disables override
toolctrl chatbot revoke --tenant acme --email bob@example.com --hard # removes the row

# Update tenant default
toolctrl chatbot set-capabilities --tenant acme --mode allow_list \
  --capabilities qa_help,kb_search,account_info

Files

  • apps/web/app/modules/chatbot/lib/capabilities/{data-read,data-write,workflow-trigger,admin-actions,external-tools}.server.ts
  • apps/web/app/modules/chatbot/lib/capabilities/{audit,rate-limit,high-risk-wrapper}.server.ts
  • apps/web/app/modules/chatbot/lib/capabilities/resolver.server.ts — V1 lookup matrix
  • apps/web/app/modules/chatbot/db/chatbot-access.db.server.ts
  • apps/web/app/routes/app.$tenant/settings/chatbot.tsx — V1 settings UI
  • packages/cli/src/commands/chatbot/access.ts
  • prisma/schema/accounts.prisma (TenantChatbotAccess, Tenant.chatbotAccessMode, Tenant.chatbotDefaultCapabilities)

Troubleshooting

  • A user sees no chatbot tools even with override. Check the master flag (toolctrl chatbot status) and the row's enabled field.
  • admin_action returns forbidden. Role gate. Only OWNER / ADMIN may run admin actions even with the capability in scope.
  • rate_limited returned from a high-risk tool. Inspect the chatbot:<capability>:<tenantId>:<userId> bucket in Redis or wait the 60 s window.

Known limitations

  • No confirmation UI step yet — the wrapper is ready to return a { ok: false, reason: 'awaiting_confirmation' } shape but the chat widget doesn't yet render it.
  • external_tools provider is a stub. Phase 07 V2 will wire the MCP client into it.