This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/docs/DESIGN-0.17.0.md
2026-02-27 16:25:39 +00:00

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 table
  • GetKBs(ctx, personaID) — returns []PersonaKB with KB metadata

KnowledgeBaseStore additions

  • ListDiscoverable(ctx, userID, teamIDs) — KBs where discoverable=true and user has access via ownership, team, or global scope
  • SetDiscoverable(ctx, kbID, discoverable) — toggle visibility
  • UpdateDocumentStorageKey(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:

  1. Load channel KBs (existing path)
  2. Load Persona KBs via PersonaStore.GetKBs()
  3. Union the two sets (deduplicated by KB ID)
  4. 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:

  1. Attempt fallback — existing behavior, unchanged.
  2. Emit eventrole.fallback on EventBus (DirToClient).
  3. Cooldown — 5-minute per-role TTL prevents flooding.
  4. Admin toast — frontend listens for role.fallback events, 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 role
  • scope=team → requires team admin role for the target team
  • scope=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_access toggle 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_bases table
  • Adds discoverable column to knowledge_bases
  • Seeds kb_direct_access platform policy (default 'true')