Skip to content

Data Model

Hierarchy

Organization (tenant boundary -- billing, auth, data isolation)
  +-- Workspace (sub-unit -- e.g., Google, Chrome, Android)
       +-- Knowledge Base (collection of documents for RAG retrieval)
            +-- Document (uploaded file)
            +-- Source (web URL / sitemap / RSS)
            +-- Chunks -> Embeddings

Core Entities

All IDs are UUIDs. All timestamps are TIMESTAMPTZ. Every tenant-scoped table carries org_id for RLS.

Organizations

Top-level tenant. Maps to Keycloak realm.

ColumnTypeNotes
idUUID PK
nameVARCHAR(255)
slugVARCHAR(100) UNIQUEURL-friendly
statusENUMactive, suspended, deactivated
settingsJSONBRate limits, feature flags

Workspaces

Sub-units within an organization.

ColumnTypeNotes
idUUID PK
org_idUUID FKTenant boundary
nameVARCHAR(255)
slugVARCHAR(100)Unique within org
settingsJSONBLLM provider selection

Knowledge Bases

ColumnTypeNotes
idUUID PK
org_idUUID FK
workspace_idUUID FK
nameVARCHAR(255)
settingsJSONBChunk size, overlap, embedding model

Documents / Sources / Chunks / Embeddings

See the full design spec for complete schemas.

Multi-Tenancy via RLS

Shared schema with Row-Level Security. All tenants share one database.

sql
ALTER TABLE workspaces ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON workspaces
    FOR ALL
    USING (org_id = current_setting('app.current_org_id')::uuid);

Go API middleware sets SET app.current_org_id = '<uuid>' on every request from JWT claims.

Document Processing State Machine

queued -> crawling* -> parsing -> chunking -> embedding -> ready
                                                           |
                                                        failed -> reprocessing

*crawling only for Sources (web scraper fetches URLs first)

Access Control

RolePermissions
ownerFull control, delete workspace
adminManage KBs, documents, members
memberRead KBs, upload, create sessions
viewerRead-only access

Four-layer enforcement: Keycloak (authn) -> API middleware (tenant scoping) -> Business logic (role checks) -> PostgreSQL RLS (defense-in-depth).