5.7 KiB
DESIGN-0.17.0 — Persona-KB Binding + Enterprise KB Mode
Overview
Personas become gateways to knowledge. In enterprise deployments, users don't interact with KBs directly — they talk to Personas that have KBs attached. This is the core differentiator for enterprise use.
Depends on: knowledge bases (v0.14.0), user groups (v0.16.0).
Design principle: Personas carry context, not users. When a user selects a Persona with bound KBs, the KB context flows automatically — no manual KB selection, no toggle management, no confusion about which KBs are in scope. Admins curate the KB↔Persona relationships; users just pick a Persona.
1. Schema
persona_knowledge_bases (new join table)
CREATE TABLE persona_knowledge_bases (
persona_id UUID NOT NULL REFERENCES personas(id) ON DELETE CASCADE,
kb_id UUID NOT NULL REFERENCES knowledge_bases(id) ON DELETE CASCADE,
auto_search BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (persona_id, kb_id)
);
auto_search controls whether the KB is searched automatically on every
message (top-K results prepended to context) or only via explicit
kb_search tool calls. Default false = tool-only.
knowledge_bases.discoverable (new column)
ALTER TABLE knowledge_bases ADD COLUMN discoverable BOOLEAN NOT NULL DEFAULT true;
When false, the KB does not appear in user-facing listings
(ListDiscoverableKBs). It remains searchable through Persona bindings.
platform_policies.kb_direct_access (new row)
Seeded as 'true' (permissive default). When set to 'false', the
channel KB popup is hidden — users access KBs exclusively through
Persona bindings.
2. Store Layer
PersonaStore additions
SetKBs(ctx, personaID, kbIDs, autoSearch)— UPSERT join tableGetKBs(ctx, personaID)— returns[]PersonaKBwith KB metadata
KnowledgeBaseStore additions
ListDiscoverable(ctx, userID, teamIDs)— KBs wherediscoverable=trueand user has access via ownership, team, or global scopeSetDiscoverable(ctx, kbID, discoverable)— toggle visibilityUpdateDocumentStorageKey(ctx, docID, key)— moved from raw SQL
3. Completion Pipeline
KB scoping in BuildKBHint
When a Persona has bound KBs, BuildKBHint merges them with any
channel-attached KBs:
- Load channel KBs (existing path)
- Load Persona KBs via
PersonaStore.GetKBs() - Union the two sets (deduplicated by KB ID)
- Build the hint string with listing
Persona ID threading
The personaID is threaded through the completion handler methods:
Complete() → streamCompletion(…, personaID) / syncCompletion(…, personaID)
→ loadConversation(…, personaID).
The ExecutionContext for tool calls carries the Persona ID so
kb_search can auto-include Persona-bound KBs in its search scope.
4. Role Fallback Alerts (issue #69)
When a role's primary provider fails:
- Attempt fallback — existing behavior, unchanged.
- Emit event —
role.fallbackon EventBus (DirToClient). - Cooldown — 5-minute per-role TTL prevents flooding.
- Admin toast — frontend listens for
role.fallbackevents, shows warning/error toast to admin users only.
Resolver changes
type Resolver struct {
stores store.Stores
vault *crypto.KeyResolver
bus *events.Bus
mu sync.Mutex
cooldown map[string]time.Time // role → last alert time
}
WithBus(bus) builder method. Nil-safe — if no bus is attached,
alerts are suppressed (log only).
5. Security Fixes
ResolvePreset bypass
ResolvePreset() in presets.go used raw SQL that skipped the
resource_grants table added in v0.16.0. A Persona accessible only
via group grant would fail at completion time. Fixed to use
PersonaStore.GetByID() + UserCanAccess().
KB create scope authorization
POST /api/v1/knowledge-bases now enforces:
scope=global→ requires admin rolescope=team→ requires team admin role for the target teamscope=personal→ open to all authenticated users
6. Frontend
KB Picker (persona-kb.js)
Standalone component: renderPersonaKBPicker(container, opts) → control
object with getValues(), setValues(), clear(), refresh().
Options: scope ('admin'|'team'|'personal'), teamId, prefix.
Shows checkboxes for available KBs with doc/chunk counts. Selected KBs
show an "Auto-inject" toggle for the auto_search flag.
Integration points
- Admin preset form: KB picker below the form, load/save bindings on
edit/create via
/api/v1/admin/presets/:id/knowledge-bases. - Team preset form: same pattern via team API prefix.
- User preset form: shows discoverable KBs only.
- Admin settings:
kb_direct_accesstoggle checkbox.
Fallback alert
Admin users receive UI.toast() notifications when role.fallback
events arrive via WebSocket.
7. API Endpoints (new)
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/presets/:id/knowledge-bases |
List KBs bound to a persona |
| PUT | /api/v1/presets/:id/knowledge-bases |
Set persona-KB bindings |
| GET | /api/v1/admin/presets/:id/knowledge-bases |
Admin: list persona KBs |
| PUT | /api/v1/admin/presets/:id/knowledge-bases |
Admin: set persona-KB bindings |
| GET | /api/v1/knowledge-bases-discoverable |
List discoverable KBs for user |
| PUT | /api/v1/knowledge-bases/:id/discoverable |
Toggle KB discoverability |
8. Migration
Single migration file: 002_v017_persona_kb.sql
- Creates
persona_knowledge_basestable - Adds
discoverablecolumn toknowledge_bases - Seeds
kb_direct_accessplatform policy (default'true')