oikb Guide

July 17, 2026 · View on GitHub

A complete guide to syncing content into Open WebUI Knowledge Bases.


Table of Contents


Installation

pip install oikb

For specific connectors with extra dependencies:

pip install oikb[gdrive]     # Google Drive
pip install oikb[s3]         # AWS S3
pip install oikb[confluence] # Confluence
pip install oikb[all]        # Everything

Requires Open WebUI 0.9.6+ and Python 3.10+.


Getting Started

Your First Sync

Set your Open WebUI credentials:

export OPEN_WEBUI_URL=http://localhost:3000
export OPEN_WEBUI_API_KEY=sk-your-api-key

Or save them permanently:

oikb config set url http://localhost:3000
oikb config set token sk-your-api-key

Find your Knowledge Base ID in Open WebUI → Knowledge → click a KB → copy the ID from the URL.

Sync a local directory:

oikb sync ./docs --kb-id your-kb-id

That's it. Only new and modified files are uploaded (SHA-256 diffing). Run it again and nothing happens — it's fully incremental.

Preview Before Syncing

Always safe to preview first:

oikb sync ./docs --kb-id your-kb-id --dry-run

This shows exactly what would be added, modified, or deleted without touching the KB.

Watch Mode

Auto-sync when files change:

oikb watch ./docs --kb-id your-kb-id

Uses filesystem events (not polling) so changes are picked up instantly.


Configuration File

For multi-source setups or daemon mode, use a .oikb.yaml config file.

Generating with oikb init

The fastest way to create one:

oikb init

This walks you through each source interactively and writes the file.

Manual Setup

Create .oikb.yaml in your project root:

sources:
  - name: docs
    source: ./docs
    kb-id: your-kb-id

  - name: wiki
    source: github:myorg/wiki
    kb-id: another-kb-id
    interval: 1h

Then sync all sources at once:

oikb sync

Global Defaults

Avoid repeating the same config across entries:

defaults:
  interval: 1h
  concurrency: 4
  filter:
    max-size: 50mb
  notify:
    url: https://hooks.slack.com/services/T.../B.../xxx
    on: error

sources:
  - name: docs
    source: github:myorg/docs
    kb-id: abc123

  - name: handbook
    source: confluence:ENG
    kb-id: def456
    interval: "0 6 * * 1-5"  # overrides default

Per-entry values override defaults. Nested dicts (filter, notify) are deep-merged.

Environment Variable Interpolation

All string values support ${VAR} and ${VAR:-default}:

sources:
  - name: docs
    source: github:${GITHUB_ORG}/docs
    kb-id: ${KB_DOCS_ID}
    token: ${GITHUB_TOKEN}
    notify:
      url: ${SLACK_WEBHOOK:-https://hooks.slack.com/fallback}

This enables GitOps workflows where .oikb.yaml is committed to the repo but secrets come from the runtime environment.


Sources

Local Directories

oikb sync ./path/to/docs --kb-id your-kb-id

Scans recursively. Supports any file type.

GitHub

oikb sync github:owner/repo --kb-id your-kb-id

Requires GITHUB_TOKEN for private repos. Syncs the default branch. To specify a branch or subdirectory:

oikb sync github:owner/repo --branch develop --path docs/

Or in .oikb.yaml:

sources:
  - name: api-docs
    source: github:myorg/api
    kb-id: abc123
    branch: main
    path: docs/

GitLab / Bitbucket

oikb sync gitlab:owner/repo --kb-id your-kb-id
oikb sync bitbucket:owner/repo --kb-id your-kb-id

Requires GITLAB_TOKEN or BITBUCKET_TOKEN respectively.

Confluence

oikb sync confluence:SPACE_KEY --kb-id your-kb-id

Requires CONFLUENCE_URL, CONFLUENCE_USERNAME, and CONFLUENCE_API_TOKEN.

BookStack

# Sync all pages from all BookStack books as Markdown files.
oikb sync bookstack: --kb-id your-kb-id

# Sync one specific BookStack page.
oikb sync 'bookstack:pages?include_ids=12' --kb-id your-kb-id

# Sync one BookStack book as chapter files; pages outside chapters remain page files.
oikb sync 'bookstack:books?include_ids=12&output=chapters' --kb-id your-kb-id

# Sync one whole BookStack book as a PDF file.
oikb sync 'bookstack:books?include_ids=12&output=books&format=pdf' --kb-id your-kb-id

# Sync all pages from one shelf and preserve shelf/book/chapter paths.
oikb sync 'bookstack:shelves?include_ids=5&structure=hierarchical' --kb-id your-kb-id

Requires BOOKSTACK_URL, BOOKSTACK_TOKEN_ID, and BOOKSTACK_TOKEN_SECRET.

Defaults are bookstack:books, output=pages, format=md, and structure=flat.

ParameterValuesDescription
outputpages, chapters, booksExports individual pages, chapters where pages are grouped in chapters, or whole books.
formattxt, md, html, pdfExported file format.
structureflat, hierarchicalKeeps files flat or mirrors shelf/book/chapter paths where available.
include_idscomma-separated IDsLimits the selected pages, books, or shelves.
exclude_idscomma-separated IDsExcludes selected pages, books, or shelves.

Use bookstack:pages, bookstack:books, or bookstack:shelves to select what IDs refer to. bookstack: defaults to bookstack:books. When bookstack:pages is used, output is always treated as pages. With output=chapters, pages outside chapters remain individual page exports.

Cloud Storage (S3 / GCS / Azure)

oikb sync s3://bucket/prefix --kb-id your-kb-id
oikb sync gcs://bucket/prefix --kb-id your-kb-id
oikb sync azure://container/prefix --kb-id your-kb-id

Uses standard cloud SDK credentials (AWS profiles, service accounts, etc.).

SharePoint

Sync a SharePoint document library:

oikb sync sharepoint:mysite.sharepoint.com --kb-id your-kb-id
oikb sync sharepoint:mysite.sharepoint.com/Documents --kb-id your-kb-id

SharePoint supports two authentication methods:

Client Secret (simpler setup)

Set these environment variables:

export SHAREPOINT_TENANT_ID=your-tenant-id
export SHAREPOINT_CLIENT_ID=your-client-id
export SHAREPOINT_CLIENT_SECRET=your-client-secret

Certificate-based auth is more secure and follows Microsoft best practices for production environments.

First install the required extras:

pip install oikb[sharepoint-cert]

Then set:

export SHAREPOINT_TENANT_ID=your-tenant-id
export SHAREPOINT_CLIENT_ID=your-client-id
export SHAREPOINT_CERTIFICATE_PATH=/path/to/cert.pem
# Optional — only needed if the PEM key is encrypted:
export SHAREPOINT_CERTIFICATE_PASSWORD=your-password

The PEM file must contain both the private key and the certificate. The connector extracts the thumbprint and signs a JWT assertion automatically.

Note: SHAREPOINT_CLIENT_SECRET and SHAREPOINT_CERTIFICATE_PATH are mutually exclusive — set one or the other.

.oikb.yaml example

sources:
  - name: legal-docs
    source: sharepoint:contoso.sharepoint.com/Legal
    kb-id: abc123
    interval: 1h

Nextcloud

Sync a Nextcloud folder via WebDAV:

export NEXTCLOUD_URL=https://nextcloud.example.com
export NEXTCLOUD_USER=svc_docs
export NEXTCLOUD_PASSWORD=your-app-password

oikb sync "nextcloud:/Documents" --kb-id your-kb-id
oikb sync "nextcloud:/Team/Engineering Handbook" --kb-id your-kb-id

Use a Nextcloud app password when possible. The connector resolves the authenticated DAV user ID through the Nextcloud OCS user endpoint, so it works with both local users and LDAP-backed accounts.

.oikb.yaml example

sources:
  - name: team-docs
    source: "nextcloud:/Documents"
    kb-id: abc123
    interval: 1h

Zotero

Sync PDF attachment text from Zotero collections into KB directories. A bare zotero: syncs all top-level collections and root library items into _unfiled; use %% for nested collections.

pip install oikb[zotero]

export ZOTERO_LIBRARY_ID=123456
export ZOTERO_API_KEY=...

oikb sync "zotero:" --kb-id your-kb-id
oikb sync "zotero:Research%%Machine Learning" --kb-id your-kb-id

Optional settings:

VariableDescription
ZOTERO_LIBRARY_TYPEuser (default) or group
ZOTERO_INCLUDE_NOTESAppend child notes when true
ZOTERO_INCLUDE_ANNOTATIONSAppend PDF annotation text/comments when true
ZOTERO_CHECKSUMversion (default) or content
ZOTERO_EXCLUDEComma-separated collection paths to skip
ZOTERO_UNFILED_DIRDirectory for root library items, default _unfiled
ZOTERO_WEBDAV_URLWebDAV Zotero storage base; fetches <attachment-key>.zip on Zotero file 404
ZOTERO_WEBDAV_USER / ZOTERO_WEBDAV_PASSWORDWebDAV credentials

All Connectors

46 connectors available. See the full list:

CategorySources
GitGitHub, GitLab, Bitbucket
Cloud StorageS3, GCS, Azure Blob, Dropbox, R2, Google Drive, SharePoint, Nextcloud, Egnyte, Oracle Cloud
Wikis & KBsConfluence, Notion, BookStack, Discourse, GitBook, Guru, Outline, Slab, Document360, DokuWiki, Google Sites
TicketingJira, Linear, Zendesk, Freshdesk, Asana, ClickUp, Airtable, ServiceNow, ProductBoard
MessagingSlack, Discord, Microsoft Teams, Gmail, Zulip
MeetingsGong, Fireflies
ForumsXenForo
Sales & CRMSalesforce, HubSpot
WebWebsite / Sitemap crawler
ResearchZotero

Filtering

Include / Exclude Globs

Control which files get synced:

sources:
  - name: docs
    source: github:owner/repo
    kb-id: abc123
    filter:
      include: ["docs/**/*.md", "*.txt"]
      exclude: ["drafts/**", "**/*.tmp"]
  • include — only sync files matching these patterns
  • exclude — skip files matching these patterns (applied after include)

Max File Size

Skip files above a size limit:

filter:
  max-size: 50mb

Accepts b, kb, mb, gb. Files exceeding the limit are warned and skipped:

⚠ Skipping model.bin (2.1 GB) — exceeds max-size (50.0 MB)

Also available as CLI flag: oikb sync ./docs --kb-id abc --max-file-size 50mb

Splitting Sources Across KBs

Route different parts of a repo to different Knowledge Bases:

sources:
  - name: user-docs
    source: github:owner/repo
    kb-id: abc123
    filter:
      include: ["docs/**"]

  - name: api-reference
    source: github:owner/repo
    kb-id: def456
    filter:
      include: ["api/**"]

Daemon Mode

For production deployments, run oikb as a long-lived daemon with scheduled sync and an HTTP API.

Starting the Daemon

oikb daemon --port 8080

Reads .oikb.yaml and syncs each source on its configured interval. Specify a config file:

oikb daemon --config /etc/oikb/config.yaml

Scheduling

Simple intervals:

interval: 30m    # every 30 minutes
interval: 1h     # every hour
interval: 6h     # every 6 hours

Cron expressions:

interval: "0 6 * * 1-5"    # weekdays at 6am
interval: "0 */6 * * *"    # every 6 hours
interval: "0 0 * * 0"      # weekly on Sunday midnight

Both can be mixed in the same config. Auto-detected by field count.

API Endpoints

EndpointDescription
GET /healthSync status for all sources (k8s readiness probe)
GET /health/readyLiveness probe
GET /metricsPrometheus metrics
GET /historySync history (filterable by KB, errors)
POST /sync/{name-or-kb-id}Trigger immediate sync
POST /sync/{id}?dry_run=truePreview changes without uploading
POST /webhooks/githubGitHub push webhook
POST /webhooks/gitlabGitLab push webhook
POST /webhooks/slackSlack event webhook
POST /webhooks/confluenceConfluence update webhook

Authentication

Secure endpoints with an API key:

export OIKB_API_KEY=your-secret-key
oikb daemon

All API requests require Authorization: Bearer your-secret-key. Docker secrets supported via OIKB_API_KEY_FILE.


Webhooks

Enable instant sync on push:

sources:
  - name: docs
    source: github:owner/repo
    kb-id: abc123
    webhook: true
    github_secret: your-webhook-secret

Then add a webhook in GitHub → Settings → Webhooks pointing to http://your-daemon:8080/webhooks/github.

Supported: GitHub, GitLab, Slack, Confluence.


Enterprise Features

Prometheus Metrics

GET /metrics exports:

MetricTypeDescription
oikb_sync_totalCounterSyncs by source and status
oikb_sync_duration_secondsHistogramSync duration
oikb_files_uploaded_totalCounterFiles added + modified
oikb_files_deleted_totalCounterFiles deleted
oikb_sync_errors_totalCounterFailed syncs
oikb_infoGaugeBuild version

Add to your Prometheus config:

scrape_configs:
  - job_name: oikb
    static_configs:
      - targets: ["oikb:8080"]

Structured JSON Logging

Enable for log aggregators (Datadog, Splunk, ELK, CloudWatch):

oikb daemon --log-format json
# or
LOG_FORMAT=json oikb daemon

Output (one JSON object per line):

{"ts":"2025-05-21T06:00:01Z","level":"INFO","logger":"oikb.daemon","msg":"Synced github:owner/repo → abc123: 3 added (1823ms)"}

Failure Notifications

POST to a webhook on sync error:

sources:
  - name: docs
    source: github:owner/repo
    kb-id: abc123
    notify:
      url: https://hooks.slack.com/services/T.../B.../xxx
      on: error     # error (default) | always

The payload includes a text field for native Slack compatibility, plus structured fields (source, status, error, duration_ms).

Sync Locking

Prevents overlapping syncs to the same KB. If a webhook fires while a scheduled sync is running, the duplicate is skipped:

INFO  Skipping github:owner/repo — sync already running for abc123

Automatic. No configuration needed.

Dry-Run via API

Preview changes without uploading:

curl -X POST http://localhost:8080/sync/docs?dry_run=true \
  -H "Authorization: Bearer $OIKB_API_KEY"

Returns:

{"dry_run": true, "result": {"added": 3, "modified": 1, "deleted": 0}}

Deployment

Docker

docker run -d \
  -e OPEN_WEBUI_URL=http://open-webui:8080 \
  -e OPEN_WEBUI_API_KEY=sk-... \
  -e OIKB_API_KEY=your-daemon-key \
  -e LOG_FORMAT=json \
  -v ./.oikb.yaml:/app/.oikb.yaml:ro \
  -p 8080:8080 \
  ghcr.io/open-webui/oikb:latest daemon

Docker Compose

services:
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "3000:8080"

  oikb:
    image: ghcr.io/open-webui/oikb:latest
    environment:
      - OPEN_WEBUI_URL=http://open-webui:8080
      - OPEN_WEBUI_API_KEY=${OPEN_WEBUI_API_KEY}
      - OIKB_API_KEY=${OIKB_API_KEY}
      - LOG_FORMAT=json
    volumes:
      - ./.oikb.yaml:/app/.oikb.yaml:ro
    command: daemon
    ports:
      - "8080:8080"
    depends_on:
      - open-webui
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1:8080/health/ready"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 15s

GitHub Actions

- name: Sync docs to Open WebUI
  uses: docker://ghcr.io/open-webui/oikb:latest
  with:
    args: sync /github/workspace/docs --kb-id ${{ secrets.KB_ID }}
  env:
    OPEN_WEBUI_URL: ${{ secrets.OPEN_WEBUI_URL }}
    OPEN_WEBUI_API_KEY: ${{ secrets.OPEN_WEBUI_API_KEY }}

Kubernetes

Minimal deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: oikb
spec:
  replicas: 1
  selector:
    matchLabels:
      app: oikb
  template:
    metadata:
      labels:
        app: oikb
    spec:
      containers:
        - name: oikb
          image: ghcr.io/open-webui/oikb:latest
          args: ["daemon"]
          ports:
            - containerPort: 8080
          env:
            - name: OPEN_WEBUI_URL
              value: http://open-webui:8080
            - name: OPEN_WEBUI_API_KEY
              valueFrom:
                secretKeyRef:
                  name: oikb-secrets
                  key: open-webui-api-key
            - name: OIKB_API_KEY
              valueFrom:
                secretKeyRef:
                  name: oikb-secrets
                  key: oikb-api-key
            - name: LOG_FORMAT
              value: json
          volumeMounts:
            - name: config
              mountPath: /app/.oikb.yaml
              subPath: .oikb.yaml
          livenessProbe:
            httpGet:
              path: /health/ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 30
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 60
      volumes:
        - name: config
          configMap:
            name: oikb-config
---
apiVersion: v1
kind: Service
metadata:
  name: oikb
spec:
  selector:
    app: oikb
  ports:
    - port: 8080

CLI Reference

oikb init                           Generate .oikb.yaml interactively
oikb sync [SOURCE]                  Incremental sync
oikb sync --dry-run                 Preview without uploading
oikb sync --max-file-size 50mb      Skip large files
oikb sync --concurrency 4           Parallel uploads
oikb sync --scan-secrets            Block files with credentials
oikb watch <dir> --kb-id ID         Auto-sync on file change
oikb daemon                         Start scheduled daemon
oikb daemon --log-format json       JSON logging
oikb daemon --config /path/to/yaml  Custom config path
oikb diff <source> --kb-id ID       Preview changes
oikb validate                       Check .oikb.yaml syntax
oikb validate --deep                Verify API, auth, and KB existence
oikb ls --kb-id ID                  List files in a KB
oikb status --kb-id ID              Show KB info
oikb history                        View sync history
oikb history --json                 JSON output
oikb history --errors               Failed syncs only
oikb reset --kb-id ID               Delete all files in a KB
oikb config set url <url>           Save Open WebUI URL
oikb config set token <token>       Save API key
oikb config show                    Show saved config

Troubleshooting

"Connection refused" or "401 Unauthorized"

Check your credentials:

oikb config show
# or verify env vars:
echo $OPEN_WEBUI_URL
echo $OPEN_WEBUI_API_KEY

"No sync entries found"

You're running oikb sync without arguments and there's no .oikb.yaml in the current directory. Either:

  • Specify the source: oikb sync ./docs --kb-id your-kb-id
  • Create a config: oikb init

Large syncs are slow

  • Enable concurrent uploads: --concurrency 4 or concurrency: 4 in yaml
  • Set filter.max-size: 50mb to skip large binaries
  • Use filter.exclude to skip unnecessary files

Daemon won't start

oikb validate        # Check config syntax
oikb validate --deep # Verify API + KB connectivity

How do I find my KB ID?

Open WebUI → Knowledge → click a KB → the ID is in the URL: http://localhost:3000/knowledge/8f3a2b1c-1234-5678-9abc-def012345678