DEEN

KnowSora

Local, private AI knowledge base — runs completely offline on your machine by default. Documents and web pages are converted into vectors and can be queried via chat with a local LLM or optionally OpenAI / Gemini / Claude.

Installation

KnowSora — Installation & Operation

KnowSora is a self-hosted AI assistant with RAG knowledge bases, nine interchangeable LLM providers (including xAI Grok and OpenRouter), a skill system with built-in tools (AI image and video generation, project file access for any AI, shell execution), project directories with universal file preview, and a web terminal for SSH access.

Documentation as of July 2026 — if something looks different in the app, the app takes precedence. Current version includes:

  • Auto-extraction of release ZIPs in manage.sh (UGREEN-compatible)
  • Auto-merge of missing ENV variables from .env.example
  • NAS sandbox auto-detection for Codex CLI
  • Header toggle on mobile for more chat space

---

Quick Start

# 1. Get the code or extract the ZIP
cd /volume3/docker/knowsora
unzip -o knowsora_release.zip   # if delivered as a ZIP

# 2. Start — manage.sh handles .env + SECRET_KEY + Docker
./manage.sh start

# 3. In the browser
http://<NAS-IP>:47822

On first start, admin / admin is created — go straight into the admin area after login and set your own password.

What ./manage.sh start does automatically:

  • Creates .env from .env.example (if not already present)
  • On later updates: new variables from .env.example are added to your

existing .env (old values are preserved)

  • Generates SECRET_KEY if empty (via openssl / python3 / /dev/urandom)

and stores it persistently in /data/.secret_key

  • Builds Docker images + starts containers
  • View logs: ./manage.sh logs

---

Requirements

  • Docker + Docker Compose v2
  • Linux host with at least 8 GB RAM (16 GB recommended if using a local LLM;

without local LLM, 4 GB is sufficient)

  • 20 GB free disk space (Docker images + models + data + AI media)
  • Optional: Intel iGPU with /dev/dri for VAAPI acceleration on the

local LLM (Intel N100, Pentium Gold 8505, N-Series)

Update Workflow

Received a new release ZIP → simply place it in the project directory and update:

cd /volume3/docker/knowsora
# place knowsora_release.zip there (e.g. via scp/SFTP/web upload)
./manage.sh update

manage.sh update automatically does:

  1. Auto-extraction of the ZIP if it's newer than manage.sh itself

(tries unzippython3pythondocker run alpine, depending on what's available on the host — also works on UGREEN/Synology without the unzip package)

  1. Merges missing ENV variables from .env.example into .env

(existing values are NEVER overwritten, only new keys are added)

  1. Codex sandbox auto-setup if the host kernel blocks

unprivileged_userns (typical for NAS)

  1. Docker build and container recreation

For build cache issues or broken images:

docker compose down
docker rmi knowsora-backend:latest knowsora-frontend:latest
./manage.sh update

Build logs are output with --progress plain — no more animated spinners, each step appears once as a plain text line (important for SSH sessions without a fully interactive TTY).

---

.env Configuration

./manage.sh start automatically creates .env from .env.example and generates the SECRET_KEY. Only edit manually if you want to change defaults (ports, data path, timeouts, provider keys in advance).

Default fields you may want to adjust:

| Variable | Meaning | Example / Default | |---|---|---| | SECRET_KEY | JWT signature + derivation of the encryption key | Auto-generated if empty | | FRONTEND_PORT | Port of the web UI | 47822 | | BACKEND_PORT | Port of the API | 48823 | | LLAMA_CHAT_PORT | Port of the local LLM | 48824 | | ADMIN_PASSWORD | Initial password for the admin user | admin (change immediately!) |

Optional but recommended:

| Variable | Default | Purpose | |---|---|---| | HOST_UID / HOST_GID | 1000:1000 | UID/GID under which the containers run — adjusts file permissions on your NAS volume | | DATA_DIR | /data | In-container path for persistent data | | MEDIA_ROOT | /data/media | AI-generated images/videos | | UPLOAD_DIR | /data/uploads | Chat attachments + provider output files | | LLM_HTTP_TIMEOUT | 3600 | Seconds — for long AI responses | | CLAUDE_CLI_TIMEOUT | 3600 | Claude Code CLI hard cap per call | | CODEX_CLI_TIMEOUT | 3600 | Codex CLI hard cap per call | | GEMINI_CLI_TIMEOUT | 3600 | Gemini CLI hard cap per call | | VEO_MODEL | veo-3.0-generate-001 | Video generation model — veo-2.0-generate-001 if Veo 3 is not enabled | | VEO_MAX_POLL_S | 600 | Maximum wait time for video rendering | | PROJECT_SHELL_TIMEOUT | 90 | Seconds — hard cap for the project_run_shell tool | | CODEX_SANDBOX_MODE | _(empty)_ | NAS workaround: danger-full-access if kernel.unprivileged_userns_clone=0 (UGREEN/Synology). Values: read-only, workspace-write, danger-full-access |

Important regarding encryption: Never change SECRET_KEY if there's already data in the DB. Otherwise all encrypted API keys become unusable and all sessions have to be logged in again. On the first start, the key is automatically persisted to /data/.secret_key — both paths (.env and .secret_key) should be part of your backup. The Fernet encryption key for API keys in the DB is derived from SECRET_KEY via PBKDF2 — there is no separate FERNET_KEY variable.

---

manage.sh — Auto Features

manage.sh is more than just a docker compose wrapper. On every start or update, the following run automatically:

1. Auto-extraction of new release ZIPs

If a knowsora_release.zip is placed in the project root that's newer than the last extracted version, manage.sh extracts it itself. Fallback chain for hosts without the unzip package:

1. unzip                  Standard on Linux with zip tools
2. python3 -m zipfile     wherever Python 3 is available (e.g. UGREEN)
3. python -m zipfile      Python 2 fallback
4. docker run alpine      last resort — Docker is there anyway

After successful extraction, the ZIP's mtime is set 24 hours in the past so the auto-trigger doesn't fire endlessly. On the next re-upload of a ZIP (newer mtime), it triggers again.

Log output:

[KnowSora] New knowsora_release.zip detected — extracting automatically...
[KnowSora]   → 'unzip' missing, using python3 zipfile
[OK] knowsora_release.zip extracted

2. ENV merge from .env.example

Adds missing ENV variables from .env.example into your .env without overwriting existing values. Only keys that are completely missing in .env are appended — commented-out values (# FOO=...) are considered "deliberately disabled" and are not activated.

Added keys are placed at the end of .env under:

# ─── Automatically added from .env.example ───

This way you automatically get all new config options on updates without losing your own values.

3. Codex sandbox auto-setup

Automatically detects when the host kernel has unprivileged_userns_clone=0 (typical for UGREEN, Synology, some Docker setups) and sets CODEX_SANDBOX_MODE=danger-full-access in .env.

Background: Codex CLI uses bubblewrap as a sandbox. On kernels without unprivileged user namespaces, bwrap fails with "No permissions to create a new namespace" and Codex aborts. The container is isolated by Docker anyway, so there's no real security loss.

Logic: only if CODEX_SANDBOX_MODE= is empty in .env AND sysctl kernel.unprivileged_userns_clone == 0. If you've set the value manually (whether danger-full-access, read-only, or something else), it won't be touched.

---

Provider Setup

KnowSora supports nine providers — all usable in parallel. Selection per chat via the provider pill in the top left.

1. Local LLM (llama.cpp)

Runs in the knowsora-llama-chat container. Model selection: place a GGUF file in /data/models/, set in .env:

CHAT_MODEL_PATH=/models/qwen3-4b-q4_k_m.gguf

Restart the container. Recommendation for 4 GB mode: Qwen3-4B-Q4_K_M. For 8 GB: Qwen3-7B-Instruct-Q4_K_M.

2. OpenAI (GPT-4o, GPT-5, o-models)

AI Models → OpenAI → API key + model ID (e.g. gpt-4o, gpt-5, o1-mini).

KnowSora automatically detects whether the model needs max_completion_tokens instead of max_tokens (gpt-5, o1, o3, o4) and whether temperature is supported. On API errors, it retries twice with the appropriate fields before giving up.

3. Anthropic Claude (API)

AI Models → Claude → API key + model ID (e.g. claude-sonnet-4-5, claude-opus-4-7).

4. Google Gemini (API)

AI Models → Gemini → API key + model ID (gemini-2.5-pro, gemini-2.5-flash, gemini-3-pro-preview).

Note: Preview models are often only enabled for certain accounts/regions. On 503 or "not found," switch to a stable model.

5. xAI Grok

AI Models → xAI Grok → API key (generate at console.x.ai). Model suggestions:

  • grok-4.3 (default) — fast + cheap, good tool calls
  • grok-4.20 — flagship reasoning
  • grok-imagine-image-quality — image generation (callable from the

media generator via provider="grok")

  • grok-imagine-video — video generation (via media generator)

Auth: xAI has no OAuth process for the API. Not even with X Premium / SuperGrok subscription. API key only. Credits must be topped up explicitly, there's no free tier.

6. OpenRouter (300+ models, free tier)

AI Models → OpenRouter → API key (generate at openrouter.ai, Google/GitHub login possible, no card required for the free tier).

What makes OpenRouter special: One API key, access to 300+ models from OpenAI, Anthropic, Google, xAI, Meta, DeepSeek, Mistral and many more — including dedicated free-tier models for completely free use (with rate limit).

Recommended model choice:

  • openrouter/free (default, killer feature) — smart routing, picks

the best free model at runtime based on the active skill: - Coding assistant → Qwen3 Coder (1M context) - Knowledge research → Llama 3.3 70B / DeepSeek V4 / Gemini 2.0 Flash - Data analyst → DeepSeek V4 Flash (reasoning) - Web research → Llama 3.3 / DeepSeek - Media generator → first general-purpose free model with tool support - Coder models are automatically excluded for non-coding skills (anti_hints in the heuristic)

  • qwen/qwen3-coder:free — top coding model, 1M context
  • deepseek/deepseek-v4-flash:free — reasoning, 1M context
  • meta-llama/llama-3.3-70b-instruct:free — solid all-purpose
  • google/gemini-2.0-flash-exp:free — multimodal, fast
  • openai/gpt-5 — paid, if credits are topped up
  • anthropic/claude-sonnet-4.6 — paid

Per-skill override: You can pin a preferred free model per skill under Skills → Edit → "🌐 Preferred OpenRouter Free Model". Smart routing then uses this instead of the heuristic. Leave empty for auto-selection.

Rate limits (as of July 2026):

  • Without credits: 50 requests/day total across all free models,

20 req/min

  • With ≥10 USD credits: 1000 requests/day on free models
  • Paid models: no OpenRouter limits, only upstream limits

KnowSora features for OpenRouter:

  • 🌐 Smart routing (openrouter/smart) — picks the best free model at

runtime based on the active skill (capability scoring by tool support, context size, model family)

  • Per-skill override — pin a favorite free model per skill
  • Live free models list in the model dropdown — dynamically loaded

via /api/v1/models (1h cache)

  • Live credits display in the provider card: current balance,

purchased and consumed credits

  • Auto-fallback on 429 — if a free model has reached its daily limit,

KnowSora automatically tries up to 2 more free models (filters for tool-calling capable ones if a skill is active)

  • App attribution — KnowSora sends HTTP-Referer +

X-Title headers so your usage appears on the OpenRouter leaderboards

Cache refresh if new free models are expected:

curl -X POST http://localhost:48823/api/openrouter/refresh-cache \
  -H "Authorization: Bearer $YOUR_KNOWSORA_TOKEN"

7. Custom OpenAI-compatible

For other hosts with OpenAI API format (Together, Groq, local APIs, etc.). AI Models → Custom → base URL + API key + model ID.

OpenRouter should be used via the dedicated provider (No. 6) — otherwise you'll miss out on free-tier detection, auto-fallback, and credits display.

8./9./10. CLI Providers (Claude Code, Codex, Gemini CLI)

Three CLIs installed locally in the backend container. Login via OAuth (free for Pro/Plus/Subscription users) or API key.

Subscription overview (no extra API costs):

| CLI | Subscription | Cost Model | |---|---|---| | Claude Code | Anthropic Pro/Team | 5-hour limits, max ~50 requests/5h | | Codex | ChatGPT Plus/Pro/Team | Included in the ChatGPT plan | | Gemini CLI | Google account (free) | 1000 requests/day with Gemini 2.5 Flash for free |

OAuth login workflow (all three the same):

  1. AI Models → CLI Provider → CLI Login
  2. Follow the instructions in the modal terminal window
  3. For Claude Code & Codex: device auth — copy the URL, log in

in the browser, paste the code back into the terminal

  1. For Gemini CLI: localhost OAuth workaround (instructions in the modal)

After successful login: KnowSora automatically detects the OAuth token periodically (every 2 sec) and closes the login window.

Important — CLI in container mode: The backend container automatically inserts a passwd entry for the container UID on start. Otherwise Node-based CLIs (especially Gemini's FileKeychain) crash with uv_os_get_passwd returned ENOENT. If there are issues: restart the backend, then ensure_passwd_entry() runs again.

---

Skills (predefined workflows)

A skill = system prompt + tool whitelist. Selectable in the skill pill at chat start. When a skill is active, the AI runs in a tool loop and can call the enabled tools.

Included default skills:

| Skill | Tools | Purpose | |---|---|---| | 🪄 Knowledge Research | search_knowledge_base, read_document, list_documents | RAG answers with source citation | | 🌐 Web Research | web_search, url_fetch, datetime_tool | DuckDuckGo + read web pages | | 📊 Data Analyst | list_documents, query_table, calculate | Analyze CSV/XLSX via SQL | | 💻 Coding Assistant | project_, python_exec, regex_test, json_tool, datetime_tool, http_request, url_fetch, web_search | Write/test code, work in the project folder | | 🤖 Claude Code | project_, all coding tools + KB search | Senior engineer persona with project tools | | 🎨 Media Generator | generate_image, edit_image, generate_video | Images/videos via OpenAI + Google + Grok | | 📄 Document Generator | generate_document, convert_document | Quotes/invoices/reports as PDF+Word, PDF↔Word conversion |

Skills are editable (Admin → Skills) — system prompt + tool list + icon + color per skill. "Reset to default" available per skill. New skills from updates are added incrementally, your customizations are preserved.

Skill tools in detail

search_knowledge_base — Hybrid search (BM25 + vector with Reciprocal Rank Fusion) in a KB. Returns top-10 chunks with source metadata (max 20). Reliably finds proper names and exact terms too, not just semantically similar content.

read_document / list_documents — Load full text of a document / list files in a KB.

query_table — DuckDB SQL over CSV/XLSX documents in a KB. Ideal for "How many orders last month?" or aggregations.

calculate — Python math expressions (sandboxed).

python_exec — Full Python sandbox in its own subprocess, with output file capture. Files created under /tmp/ automatically appear as a download button in the chat.

web_search / url_fetch — DuckDuckGo search + read web pages as Markdown (BS4 + Readability).

http_request — Arbitrary HTTP calls (GET/POST/PUT/DELETE) with headers + body. Whitelist against internal IPs.

json_tool / regex_test / datetime_tool — Helper functions for parsing, pattern matching, time calculations.

Project tools (NEW — available for ANY provider)

These tools allow any AI (also OpenAI/Claude/Gemini API, not just CLIs) to work directly in the current project directory:

project_list_dir — Show directory contents (max 500 entries, optionally recursive).

project_read_file — Read file up to 1 MB (optional max_lines). No write permissions needed.

project_write_file — Write or append to a file, max 5 MB. Directories are auto-created. Requires enabled write permissions in the project.

project_delete — Delete file or directory (recursive for folders). Requires enabled write permissions.

project_run_shell — Run a shell command in the project's cwd (npm install, python -m pytest, git status, etc.). Hard timeout 90 sec (PROJECT_SHELL_TIMEOUT changeable), stdout/stderr capped at 50 KB. Requires enabled write permissions.

Write permissions toggle per project — same logic as for the CLIs:

  • Toggle OFF → write/delete/shell fail with a clear error

message, list/read still work

  • Toggle ON → full access rights in the project folder

Cross-provider workflow: This lets GPT-5 check what Claude Code just built in the project, or Gemini review a Python script created by Claude for bugs.

Media tools

generate_image — AI image generation. Parameter provider:

  • openai (default) — gpt-image-1, photorealistic, possibly org verification
  • gemini — imagen-4.0-generate-001, strong artistic quality
  • grok — grok-imagine-image-quality, photorealistic + strong text rendering

edit_image — Image-to-image based on an uploaded photo. Automatically uses the first image attachment of the current message (attachment_index=0). JPEGs are converted to PNG server-side (Pillow) before being sent to OpenAI's /images/edits endpoint.

  • openai — gpt-image-1 edits, photorealistic
  • gemini — gemini-2.5-flash-image (Nano Banana), creative
  • grok — grok-imagine-image-quality edits, base64 data URI as input

generate_video — Video generation. Parameter provider:

  • gemini (default) — Google Veo 3, 4-8 sec, approx. 1-3 min wait time
  • grok — grok-imagine-video, 5-15 sec, ~30 sec wait time

OpenAI and Anthropic have no public video API. Sora is only available in ChatGPT, not via API.

Where do generated media end up?

Three storage locations in parallel:

/data/media/<user_id>/<YYYY-MM-DD>/<uuid>.png       # Original storage
/data/uploads/...                                    # Provider output files
<project>/outputs/<YYYY-MM-DD>/<original_name>      # If chat has a project

Every generated file is persisted as a ChatOutput DB row — remains visible in the chat history after tab switch and browser restart, with inline preview (images: <img>, videos: <video controls>).

If the chat has a project assigned:

All outputs are additionally automatically saved as a copy in the project directory (<project>/outputs/<date>/). On name conflicts, a counter is appended (image.pngimage (1).png).

The project copy is completely independent of the DB: deleting the chat, deleting the ChatOutput, deleting the original in /data/media/ — none of this affects the project copy. Only deleting the project itself (or manually via the file browser) removes it.

Copy retroactively: In the chat header, there's a button "→ Project" (appears only if a project is assigned). This copies all existing outputs of the chat into the project at once — useful for outputs that were created BEFORE the project assignment.

---

Knowledge Bases (RAG)

  1. Knowledge DBs → Create a new KB (name, icon, color)
  2. Documents → Upload files, assign to a KB
  3. In the chat pill, select KB → from now on the skill automatically

uses this KB (provided the skill includes search_knowledge_base)

Supported formats: PDF, DOCX, XLSX, CSV, TXT, MD, HTML, JSON, PPTX.

Indexing: runs as a background worker. Status in the document listing (pendingprocessingready).

Embedding model: default nomic-embed-text-v1.5 (768-dim, ONNX, in-process via fastembed — no separate container). Switchable in the admin area under "Embed Models" (triggers auto-reembed of all documents).

---

Projects (Cross-Provider Workspaces)

Projects are persistent working directories per user under /data/projects/<user_id>/<slug>/. When activated as a project pill in the chat, the AI uses this directory as cwd for Claude Code, Codex, Gemini CLI and for the project_* tools (any provider).

Functions per project:

  • File browser with universal preview:

- Images (png/jpg/gif/webp/svg/avif/bmp): <img> inline - Videos (mp4/webm/mov/mkv/avi): <video controls> - Audio (mp3/wav/ogg/flac/m4a/aac): <audio controls> - PDF: <iframe> (75vh tall) - Text/code (50+ extensions — py/js/ts/json/md/csv/sql/yaml etc.): <pre> - Office/archive/binary: info box with download button

  • File download per file (bearer auth via blob + object URL)
  • ZIP upload automatically extracts into the project directory
  • ZIP download downloads the whole project (with auto-excludes:

node_modules, .git, .venv, dist, build, __pycache__ etc.) - Default: hidden files excluded - ?include_hidden=true for backups including .env

  • Write permissions toggle — when OFF, all CLIs and project_* tools may

only read: - Claude Code CLI: --permission-mode=default + disallowed tools - Codex CLI: --sandbox=read-only - Gemini CLI: --approval-mode=plan + allowed-tools whitelist - project_* tools: write/delete/shell → clear error message

Outputs subfolder: <project>/outputs/<date>/ contains all images/videos/files generated by the chat in skill mode. Automatically populated on creation, can also be filled retroactively for existing outputs via a button in the chat header.

---

Web Terminal (Wetty)

For SSH access to the NAS directly from the browser. Log in with the SSH credentials of the NAS user.

Configuration: Admin → Web Shell → set SSH host, port, and default user. NAS-specific SSH port (e.g. 50199 instead of 22) is supported.

---

Live Activity Box (CLI Visibility)

While Claude Code, Codex, or Gemini CLI are actively working, the user sees live what the CLI is doing:

●  📖 Reading src/auth.ts
   ✏️ Editing schema.prisma
   🔧 Running `npm install`
   ✅ Command completed

Works in all skill and classic modes. The box appears automatically when the job runs, disappears after done. Current action is highlighted with a pulsing dot.

---

Provider Output Files (download directly in chat)

When a provider creates a file in the container and mentions a path in the response, a download button automatically appears under the assistant bubble.

Detection via:

  • Explicit marker: {{download:/tmp/foo.html}} or

[[download:/tmp/foo.html]]

  • Auto-detection: absolute paths in code blocks or text that

reference an allowed file

Allowed source directories: /tmp/, /var/tmp/, /data/.claude_home/, /data/outputs/, /home/, /data/media/

System directories (/etc/, /usr/, /proc/, /root/, /var/log/) are ignored.

Limits: 50 MB per file, max 10 files per response, ~50 file extensions on the whitelist.

---

Voice (speech input and output)

Since May 2026, KnowSora supports speech-to-text (STT) and text-to-speech (TTS) with three selectable engines.

Engines

| Engine | STT | TTS | Cost | Latency | Requirement | |--------|-----|-----|--------|--------|---------------| | Browser (default) | Web Speech API | Web Speech API | free | <500ms | Chrome/Safari/Edge | | OpenAI | Whisper-1 | TTS-1 (MP3 streaming) | ~0.006 USD/min STT, ~0.015 USD/1k chars TTS | 1-2s | OpenAI provider with API key | | Gemini | gemini-2.5-flash | gemini-3.1-flash-tts-preview | free tier | 3-8s | Gemini provider with API key |

Browser engine is the default choice and works best on iPhone/iPad with Apple voices. Limited under Linux/Firefox — switch to OpenAI or Gemini there.

Voices

  • OpenAI: alloy, echo, fable, onyx, nova, shimmer
  • Gemini: Kore, Puck, Charon, Fenrir, Aoede, Leda, Orus, Zephyr
  • Browser: all system-installed voices

Activation

Voice is always built in — no backend configuration needed. A microphone icon appears in the chat input line. Voice engine + voice + language can be set per browser profile in the Voice Settings popover (gear/⋮ menu).

HTTPS requirement

Important: Browsers only allow microphone access over HTTPS or localhost. For self-host setups with a reverse proxy, be sure to set up Let's Encrypt (see "Reverse Proxy" section), otherwise voice input won't work.

IT terms pronunciation dictionary

KnowSora has a built-in dictionary that automatically correctly pronounces ~50 tech abbreviations:

  • VLAN → "V Lan", DHCP → "D H C P", WLAN → "We Lan"
  • JSON → "Jason", NAS → "Nas", RAID → "Reid", NAT → "Nat", MAC → "Mac"
  • IP addresses are read out dot-separated
  • MAC addresses hex pair by pair
  • URLs are summarized as "Link", code blocks skipped

TTS limits

  • Max 4096 characters per TTS request (KnowSora automatically chunks

longer texts and plays them one after another)

  • iOS Safari: audio playback only allowed directly within a user gesture

context — the first speaker tap per tab must be manual

---

Hybrid Search (BM25 + Vector)

Since May 2026, search_knowledge_base uses hybrid retrieval instead of pure vector search. No configuration needed — always active.

What happens?

Each search runs in parallel across two engines:

  1. Vector search (Chroma + fastembed) — finds semantically related

content, even with synonyms and paraphrases

  1. BM25 in-memory — finds exact word matches,

proper names, IDs, codes

The two ranked lists are fused into a single top list using Reciprocal Rank Fusion (RRF). Tokenization: lowercase, German + English stopwords removed, minimum length 2 characters.

Cache behavior

The BM25 index is kept in memory per KB filter and automatically invalidated on:

  • New chunks (doc upload, reindex)
  • Doc delete
  • KB delete

The first search call after cache invalidation rebuilds the index (<100ms for 10k chunks).

Filename prefix during embedding

Additionally, the filename is included as a header in the embed text: [Source: filename.pdf]\n\n<chunk>. This means terms that only appear in the filename are embedded too and can be found via vector search for the correct file.

The stored chunk text remains original — users don't see the prefix in the research results.

Important: Documents uploaded BEFORE May 2026 don't have the filename prefix. Via KB Edit → "🔄 Re-index N existing documents", all docs in a KB can be reprocessed with current settings.

---

Contextual Retrieval (Smart Chunking)

Optional, activatable per KB. Based on the Anthropic method anthropic.com/news/contextual-retrieval.

How does it work?

During indexing, a 1-2 sentence context is generated per chunk via LLM from the overall document and prepended to the chunk — but only for embedding. The stored chunk remains original.

Example:

Chunk: "The result was 5.2 million EUR in Q3."

Embed input (with contextual): "This chunk is from the Q3 2024 report of ACME GmbH and describes the quarterly revenue. The result was 5.2 million EUR in Q3."

Result: hit relevance improved by 30-50% for questions like "What was ACME's revenue in Q3?".

Activation

Open KB → Edit → enable "🧠 Contextual Retrieval (smart chunking)" → choose LLM provider → save.

Provider choice per KB:

  • OpenRouter (default, free, ~3-15 min for 100 chunks)
  • OpenAI gpt-4o-mini (~0.50 USD for 500 chunks, very fast)
  • Groq Llama 3.3 70B (free, very fast)
  • Gemini Flash (free in the free tier)
  • DeepSeek

Reindexing existing documents

Via the "🔄 Re-index N existing documents" button in the KB Edit modal. All docs are set to pending and reprocessed by the worker — this time with contextual retrieval.

Robustness

  • Retry logic on rate limits (429) with exponential backoff:

5s → 15s → 30s → 60s (max 5 attempts per chunk)

  • Parallelism limited to 4 concurrent LLM calls
  • Fallback to normal embedding if context LLM fails persistently
  • Worker timeout increased to 2h (default), overridable via

DOC_PROCESS_TIMEOUT_MAX in .env

Performance

| Doc size | Time with contextual (OpenRouter free) | |-----------|---------------------------------------| | 5 chunks | ~30-40s | | 20 chunks | ~2-3 min | | 100 chunks | ~10-15 min | | 500 chunks | ~45-60 min |

With OpenAI gpt-4o-mini, about 5-10x faster.

---

Mobile Optimizations

KnowSora is designed responsively and uses an adapted layout under 768px viewport width:

Collapsible header. On mobile, the selector row (Provider/Skill/Project/KB) and the attachment bar are collapsed by default — only the title and action buttons are visible. Maximum room for the chat. The chevron button next to "Clear" toggles the options on and off.

Mobile-safe selectors. Provider/Skill/Project/KB dropdowns use min(280-360px, calc(100vw - 24px)) as width with maxHeight: 70vh and scroll — no overflowing at the screen edge.

Admin user list as cards. Instead of the desktop table, on mobile a compact card appears per user with avatar, username, email, role/status badges, and edit/delete buttons in a row.

Provider cards stacked. Long provider names (e.g. "OpenAI (GPT-4o, etc.)") aren't truncated — on mobile the title and config buttons appear one below the other instead of side by side.

AI Models menu visible only to admins. End-customer users don't see the entry. They select providers directly in the chat dropdown — the list shows only providers with usable=true (API key present or OAuth active). API keys are never returned to the frontend.

Voice on mobile. Microphone icon remains prominent in the main row. Other voice options (auto read-aloud, voice settings, project attachment) are in the overflow menu (⋮) so the input field stays large. Voice settings as a bottom sheet with large touch targets instead of a popover.

---

Reverse Proxy (Nginx Proxy Manager)

If KnowSora runs behind NPM or another proxy, the following timeouts must be increased, otherwise long AI requests get killed:

NPM → Proxy Host → Advanced → Custom Nginx Configuration:

# Timeouts for long AI/CLI requests (up to 1 hour)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_connect_timeout 60s;

# Uploads up to 200 MB
client_max_body_size 200M;

# WebSocket for CLI login terminal
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

# Standard headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# Don't buffer streaming
proxy_buffering off;
proxy_cache off;

Frontend polling logic automatically does exponential backoff (800ms → 1.6s → 3.2s → 6.4s → max 8s) on repeated network errors, so the browser console doesn't spam during Let's Encrypt cert renewals.

---

Robustness & Error Handling

ErrorBoundary

Render errors in the frontend are caught — instead of a blank screen you see a fallback UI with:

  • "Try again" button (resets the component tree)
  • "Reload page" button
  • Stack trace in an expandable <details>

Global window.error + unhandledrejection listeners also log to localStorage["knowsora-errors"] (last 30 events) for post-mortem diagnosis.

Auto-retry for LLM API quirks

On OpenAI 400 errors:

  • "temperature does not support 0" → retry without temperature
  • "max_tokens not supported, use max_completion_tokens" → retry with

the field switched

On Gemini 400:

  • "temperature not supported" → retry without temperature

Clear translation of 404/503 errors into user-friendly messages with a hint on which model works instead.

Background job system

Chat requests run as a background job:

  • Frontend polls /api/chat/job/{id} every 800ms → reads status +

activity events live

  • Job IDs are cached in localStorage["kh-job-<chat_id>"] → closing

the tab and reopening it reconnects seamlessly

  • Cancel button hard-aborts the running CLI subprocess

Mobile edit bug

Fixed in this version: user messages are shown optimistically with id: Date.now(). The real backend ID is patched back into the state via .then(). On very fast edits right after sending, there's a 404 fallback that re-saves the message and continues locally.

Soft auth expiry

On JWT expiration (HTTP 401), it doesn't navigate hard via window.location.href — instead a custom event knowsora-auth-expired → React Router does the redirect to /login without a page reload (no temporary blank screen).

---

API Endpoints (for custom development)

| Endpoint | Method | Purpose | |---|---|---| | /api/auth/login | POST | Login (form-encoded), returns JWT | | /api/auth/me | GET | Current user | | /api/chats/ | GET/POST | Chat list / create new chat | | /api/chats/{id} | PUT/DELETE | Change chat / delete | | /api/chats/{id}/messages | GET | Load messages of a chat | | /api/chats/{id}/messages/{msg_id} | PUT | Edit user message + delete subsequent ones | | /api/chats/{id}/copy-outputs-to-project | POST | Copy existing chat outputs into the project | | /api/chat/start | POST | Start an AI request as a background job | | /api/chat/job/{job_id} | GET | Poll job status + activity events | | /api/chat/job/{job_id}/cancel | POST | Cancel a running job | | /api/skills/ | GET / POST / PUT / DELETE | Skill management | | /api/skills/tools | GET | Tool catalog | | /api/knowledge-bases/ | GET/POST/PUT/DELETE | KB management | | /api/documents/ | GET/POST | Document list / upload | | /api/projects/ | GET/POST | Project list / create | | /api/projects/{id}/browse | GET | Directory contents | | /api/projects/{id}/file | GET/DELETE | Read / delete file, ?download=1 for FileResponse | | /api/projects/{id}/upload | POST | Upload a single file | | /api/projects/{id}/upload-zip | POST | Upload + extract ZIP archive | | /api/projects/{id}/download-zip | GET | Download the whole project as ZIP | | /api/projects/{id}/mkdir | POST | Create directory | | /api/message-attachments/upload | POST | Upload image/file for the next message | | /api/message-attachments/{id} | DELETE | Delete attachment | | /api/chat-outputs/{id}/download | GET | Download provider-generated file | | /api/media/{user_id}/{path} | GET | AI-generated media (auth + owner check) | | /api/ai-providers/ | GET/POST/PUT/DELETE | Provider management | | /api/ai-providers/{id}/test | POST | Test call to provider | | /api/cli-login/start/{cli} | POST | Start OAuth login session | | /api/cli-login/ws/{cli} | WS | Terminal stream for CLI login | | /api/cli-login/status/{cli} | GET | Current OAuth status | | /api/embed-models/catalog | GET | Available embedding models | | /api/embed-models/activate | POST | Switch embedding model | | /api/admin/users | GET/POST/PUT/DELETE | User management |

JWT token as Authorization: Bearer <token> header.

---

Architecture

                 ┌──────────────┐
                 │ Nginx Proxy  │
                 │   Manager    │
                 │  (optional)  │
                 └──────┬───────┘
                        │
       ┌────────────────┼────────────────┐
       │                │                │
┌──────▼───────┐ ┌──────▼───────┐ ┌──────▼───────┐
│  Frontend    │ │   Backend    │ │  llama-chat  │
│ Vite+React   │ │  FastAPI     │ │  llama.cpp   │
│ Port 47822   │ │  Port 48823  │ │  Port 48824  │
└──────────────┘ └──────┬───────┘ └──────────────┘
                        │
              ┌─────────┼─────────┐
              │         │         │
       ┌──────▼──┐ ┌────▼────┐ ┌──▼───────┐
       │fastembed│ │ChromaDB │ │ DuckDB   │
       │ (ONNX,  │ │persist  │ │:memory:  │
       │in-proc) │ │         │ │query_tbl │
       └─────────┘ └─────────┘ └──────────┘
                        │
                ┌───────┴────────┐
                │   SQLite       │
                │ /data/         │
                │  knowsora.db   │
                └────────────────┘

All containers in host network mode — no port mapping, direct LAN access.

Data persistence — everything under /data/ (or the path from DATA_DIR):

  • knowsora.db — main DB
  • chroma/ — vector store
  • models/ — GGUF files
  • uploads/ — chat attachments + provider output files
  • media/<user>/<date>/ — AI images/videos (persistent independent of chat)
  • projects/<user>/<slug>/ — coding projects
  • projects/<user>/<slug>/outputs/<date>/ — automatic backup copies of all chat outputs for this chat
  • .claude_home/, .codex/, .gemini/ — CLI OAuth tokens

Backup recommendation: Back up the entire /data path PLUS the .env (contains SECRET_KEY) PLUS /data/.secret_key (persistence copy of the key). When restoring, the SECRET_KEY must be identical to the original installation, otherwise encrypted API keys and sessions become unusable.

---

Troubleshooting

Backend won't start, logs show "permission denied" on /data: HOST_UID/HOST_GID in .env don't match the volume owner. Fix:

chown -R 1000:1000 /volume3/docker/knowsora/data

Gemini CLI: "uv_os_get_passwd returned ENOENT": Container UID has no passwd entry. Should be automatically fixed at backend start. If not: docker compose down && docker compose up -d --build (no --no-cache needed — Dockerfile has chmod 666 /etc/passwd /etc/group).

Gemini CLI: "folder is not trusted": Automatic entry in trustedFolders.json fails. Check logs. Workaround: restart the backend container — on the next tool call the entry is rewritten.

OpenAI: "max_tokens is not supported, use max_completion_tokens": Should be automatically retried. If it persists: AI Models → OpenAI → check which model is configured. For gpt-5, o1, o3, o4, max_completion_tokens is mandatory.

Veo video: "Model not found / 503":

  • Check model name: veo-3.0-generate-001 (premium) or

veo-3.0-fast-generate-001 (cheaper) or veo-2.0-generate-001 (fallback)

  • API key for Google AI Studio needs Veo enablement
  • Tool parameter model: "veo-2.0-generate-001" as override

Images/videos disappear after tab switch: Shouldn't happen anymore with the current version — media outputs are persisted as ChatOutput DB rows. If it still happens: old images (from before the update) aren't migrated; new generations remain.

Project tools "No project assigned": For the project_* tools, a project must be explicitly selected in the project pill at the top of the chat. Switching to a chat without a project disables the tools (they return clear error messages).

Project_run_shell fails with "write permissions disabled": Enable "write permissions" in the project editor. Same toggle as for the CLI providers (Claude Code, Codex, Gemini CLI).

Codex CLI: "bwrap: No permissions to create a new namespace": Host kernel has kernel.unprivileged_userns_clone=0. Typical for UGREEN, Synology, some embedded Linuxes. manage.sh update sets this automatically via CODEX_SANDBOX_MODE=danger-full-access in .env. If detection doesn't kick in, manually:

echo "CODEX_SANDBOX_MODE=danger-full-access" >> .env
docker compose up -d --force-recreate backend
docker exec knowsora_backend env | grep CODEX_SANDBOX_MODE

Codex CLI: "Reading additional input from stdin..." (exit 1): Fixed in the current version — Codex' argument parser can get confused by leading -/-- in the prompt and falls into interactive stdin mode. Fix: -- as a separator before the prompt (codex exec ... -- "<prompt>").

Updated but frontend shows the old version:

  • First verify on the NAS whether the new code is in the container:

  docker exec knowsora_frontend sh -c 'ls /usr/share/nginx/html/assets/index-*.js'
  
Hash should change after update.

  • If hash is the same: ZIP wasn't extracted → manually:

unzip -o knowsora_release.zip && ./manage.sh update

  • If hash differs: browser cache. Mobile Safari: close app +

reopen or iOS → Safari → Advanced → Clear Website Data

Backend logs:

./manage.sh logs            # all containers, live
docker logs -f knowsora_backend
docker logs -f knowsora_frontend

Filter in the backend log: [worker], Job <id>, Skill '<name>', OpenAI, Gemini, Claude, Grok, project_.

---

Atlassian Integration (Jira + Confluence)

KnowSora can connect to Atlassian Jira and Confluence — entirely via the web GUI, without any Docker/.env intervention and without Atlassian admin rights. Both use the same API token and access read-only — nothing is changed or deleted in Atlassian.

Two usage modes:

  1. Live tools in chat — the connected access provides the search tools

jira_search (JQL), confluence_search (wiki full-text) and confluence_get_page. Enable in a skill (Admin → Skills → Tools), then the AI searches Jira/Confluence live.

  1. RAG sync — under "Integrations → Knowledge Sync", create a source:

a Jira project (by project key, e.g. HOTSPOT) or a Confluence space (by space key, e.g. ENZY from the URL …/wiki/spaces/ENZY/…). The content is periodically indexed into a knowledge base and then searchable via the normal knowledge search.

Setup (one-time, by a KnowSora admin):

  1. Create an API token at Atlassian — any regular user can do this,

without admin rights: https://id.atlassian.com/manage-profile/security/api-tokens → "Create API token". The same token applies to Jira and Confluence.

  1. In KnowSora: menu "Integrations" → block "Jira Connection (Admin)":

enter site URL (e.g. https://frederix-hotspot.atlassian.net), account email and API token.

  1. "Save", then "Test connection".

The token is stored encrypted in the database (no plaintext) and serves as a shared read access for all users of the instance.

Usage (by any user):

  • Under "Knowledge Sync" → "Add source", choose source type

(Jira project or Confluence space), specify key and target knowledge base, choose interval (manual up to weekly). Refresh button triggers a sync immediately.

  • In chat (with a skill that has the Atlassian tools enabled), directly ask

about tickets or wiki content.

Notes:

  • Microsoft/SharePoint, GitLab, and GitHub are not included — the

integration is focused on Atlassian.

  • Background: Atlassian discontinued the old Jira search endpoint on

May 1, 2025. KnowSora uses the current /rest/api/3/search/jql endpoint with token pagination.

License & Contact

KnowSora is developed and sold by localeye.shop. Licensing questions, support, custom adaptations: https://localeye.shop or email the provider.

---

ADDENDUM (2026-07-03): Memory (persistent user memory)

New menu item Memory (🧠) — permanent facts about the user that are automatically included as context in every chat and with every AI model (provider/model-switch-independent).

Automatic extraction

After each saved AI response, the backend checks whether at least 6 new messages have been added to this chat since the last check (FACTS_TRIGGER_MSGS). If so, the new conversation excerpt is sent to an LLM which extracts permanent facts from it and assigns a category: fact / preference / project / person. Progress is tracked per chat (chat.facts_upto_msg_id) so the same section isn't analyzed repeatedly.

Autosync button (retroactive extraction across all chats)

The running auto-trigger only covers the currently open chat. Older or very short chats (that never crossed the 6-message threshold) are never captured. The Autosync button on the Memory page catches this up retroactively across all of the user's chats:

  • Runs in chunks (24 messages per LLM call, max 25 calls per

backend request) so a single request doesn't run indefinitely long. The frontend automatically calls the endpoint again in multiple rounds until complete: true is returned — no manual multiple clicking needed.

  • While running, the button shows a live status line ("X chat(s)

searched, Y facts total") and the current round, and is visually clearly recognizable as active (filled "primary" button instead of a hard-to-recognize default state).

  • Progress is also tracked via chat.facts_upto_msg_id — a

repeated click at a later time only searches what has been newly added since then.

Categories & display

Facts are grouped by category on the page with a colored section heading (fact=gray, preference=blue, project=green, person=orange) instead of a single, uniform-looking list. Each entry additionally shows via icon whether it was created automatically (✨) or manually (👤), and can be edited, hidden ("soft forget") or deleted.

Re-categorization (one-time follow-up run)

Facts that were automatically extracted before this feature expansion were all hard-set to the category "fact". The Recategorize button (appears once at least one fact exists) sends all existing facts to the LLM once and reassigns the appropriate category to each — repeatable as often as needed, without duplicate processing.

"Delete all" / "Reset sync"

The delete button has two states: if facts exist, it deletes all of them and simultaneously resets the sync progress of all chats (facts_upto_msg_id = NULL) so Autosync starts from zero again afterward. If no facts exist, the button remains visible (label "Reset sync") and only resets the progress pointers — without this, an empty memory with a far-advanced sync pointer would be a deadlock: Autosync would forever find 0 new messages, and there would be no way to fix this via the UI.

API endpoints

GET    /api/user-memory/             — own facts (newest first)
POST   /api/user-memory/             — create fact manually
PUT    /api/user-memory/{id}         — edit / (de)activate fact
DELETE /api/user-memory/{id}         — delete fact
DELETE /api/user-memory/             — delete all own facts + reset sync pointer
POST   /api/user-memory/sync         — autosync (retroactive extraction across all chats)
POST   /api/user-memory/recategorize — one-time recategorization run

Upper limit: max 80 facts per user (oldest must be manually deleted to make room), max 400 characters per fact.

---

ADDENDUM (2026-07-02): Document Generator, Auto-Scroll & SQLite WAL

📄 Document Generator (Skill)

New default skill Document Generator (generate_document, convert_document):

  • generate_document — creates quotes, invoices, reports and

cover letters as PDF and/or Word (title, intro lines, sections with body text/bullets, line-item table with total row, footer).

  • convert_document — converts a file uploaded in chat

between PDF and Word. pdf_to_word runs purely in Python (pdf2docx), word_to_pdf uses LibreOffice headless in the container.

  • If the user wants to send a quote/invoice by email, the AI

additionally automatically writes a ready-made, copy-paste-ready email text (subject, salutation, body text, closing) directly into the response.

Requirement: The system package libreoffice-writer is installed in the image (Dockerfile ARG INSTALL_LIBREOFFICE=true). Without this package, word_to_pdf returns a clear error message instead of crashing; building with INSTALL_LIBREOFFICE=false saves about 350–450 MB of image size, then only pdf_to_word is available.

Chat auto-scroll

The chat now automatically scrolls smoothly downward during a running AI response — no more manual scrolling needed. If the user scrolls up themselves in the meantime, auto-scroll pauses (no yanking out of the history) and a "Jump to end" button appears at the bottom right.

SQLite WAL mode (stability)

The database now runs in WAL journal mode with busy_timeout=30000 instead of the standard DELETE mode. Reduces sporadic "database is locked" errors with multiple simultaneous background jobs (polling, streaming, attachments).

Enterprise / multi-user operation (PostgreSQL, optional)

Problem with SQLite: SQLite serializes write access — even with WAL mode (see above), it remains the bottleneck once many employees are chatting simultaneously, uploading documents, or editing KBs (heavy parallel write access). For individual users up to small teams, SQLite remains completely sufficient and remains the default — nothing changes for existing installations as long as nothing is configured.

Solution: KnowSora now optionally supports PostgreSQL as a database backend. Postgres uses row-level instead of DB-wide locking and thus scales significantly further for parallel multi-user load. The switch is purely configuration-based — the SQLAlchemy models remain unchanged and DB-agnostic.

Existing installation → automatic (default case, no manual entry needed): ./manage.sh update checks on every run whether the old SQLite file (${DATA_DIR}/data/knowsora.db) with real data still exists and DATABASE_URL doesn't yet point to Postgres. If so, it without asking:

  1. Generates a random, secure password (openssl rand -hex 24).
  2. Enters POSTGRES_PASSWORD, DATABASE_URL (user knowsora) and

COMPOSE_PROFILES=postgres into .env.

  1. Starts the Postgres container, waits for healthy.
  2. Transfers all tables from the SQLite file once (users, chats,

skills, KB metadata; sequences continue correctly) — before the backend starts against Postgres, so no duplicate admin user is created.

  1. Continues building and starting normally afterward — from now on, the

installation runs permanently on Postgres.

The generated Postgres password is then in .env under POSTGRES_PASSWORD (not the same as the web GUI admin login).

Manually enabling for a new installation (only relevant if Postgres is desired from the start instead of SQLite, without existing SQLite data):

  1. In .env: set POSTGRES_PASSWORD, enable COMPOSE_PROFILES=postgres,

enter DATABASE_URL=postgresql+asyncpg://knowsora:<PASSWORD>@127.0.0.1:<POSTGRES_PORT>/knowsora (example lines already in .env.example).

  1. ./manage.sh update

Details:

  • The postgres container starts only if COMPOSE_PROFILES=postgres

is set — without this line (or as long as no old SQLite installation with data is detected), everything remains on the SQLite default, no additional container, no risk to existing setups.

  • Postgres data is stored persistently under ${DATA_DIR}/data/postgres.
  • Pool size (DB_POOL_SIZE/DB_MAX_OVERFLOW, default 10/20) is

configurable in .env — higher for very many simultaneous users.

  • ./manage.sh reembed / embed-reset work with Postgres the same

as with SQLite (use the normal DB engine internally, no more hard-wired SQLite access).

  • ./manage.sh migrate-to-postgres remains available as a manual single

command (e.g. for a repeat/manual migration) — normally ./manage.sh update handles it automatically, see above.

  • Robust against interruptions: whether migration is still really needed is

checked freshly by ./manage.sh update on every run based on two things — COMPOSE_PROFILES=postgres active and no marker ${DATA_DIR}/data/.postgres_migrated present. If a previous update run had already gotten as far as switching .env to Postgres, but the actual data transfer was interrupted (crash, restart mid- deploy, etc.), the next update run automatically catches this up — the marker is only set after successful completion. The migration run itself is idempotent (clears the target tables before each (re-)import), so a retry doesn't lead to duplicate data.

  • If POSTGRES_PASSWORD/DATABASE_URL are missing in an existing .env

even though COMPOSE_PROFILES=postgres is already set (e.g. incomplete manual setup), ./manage.sh update automatically adds them with a newly generated password — again without manual entry.

  • The reverse path (Postgres → SQLite) is not automated — manually

re-export data if needed.

---

ADDENDUM (2026-06-14): New Features & Fixes

Billing display in chat (OAuth = free / API = costs)

In the active chat, the badge next to the AI provider selector now clearly shows which mode is being used:

  • Free (green label) — OAuth token active, no token consumption
  • Token (orange label) — API key is being used, costs are incurred

Visible directly next to the provider pill in the top left of the chat — without having to go into settings. Applies to Claude Code, Codex, and Gemini CLI in all auth modes.

On the AI Models page (admin area), a billing status line also appears per CLI provider: "OAuth active (free)" or "API key active (paid)".

Live update on automatic fallback (auth mode "Automatic"): If, during a running chat, the OAuth session limit is reached (or the token has expired/is invalid), KnowSora automatically switches to the stored API key mid-response — and the badge jumps from "Free" to "Token" at that same moment, without reload or chat switch. The backend reports actual_billing per response (what was actually used), the frontend immediately adopts this value into the badge. Only affects the "Automatic" auth mode — with "OAuth only"/"API key only", the badge naturally stays fixed, since no fallback occurs there.

OpenRouter 429 → Clear error message

When an OpenRouter model has reached its daily limit (HTTP 429), a comprehensible message now appears instead of raw JSON:

OpenRouter limit reached (429): This model is exhausted for today.
KnowSora is automatically trying a fallback model …

KnowSora then automatically tries up to 2 more free models (with tool-calling support if a skill is active).

Codex bwrap → auto-retry

If Codex fails with "bwrap: No permissions to create a new namespace", KnowSora automatically retries the call with --sandbox danger-full-access — without a restart and without manual .env changes. The previous manage.sh mechanism (CODEX_SANDBOX_MODE) remains as a persistent default setting, the auto-retry additionally catches all runtime exceptions.

Limit message without raw JSON details

Provider error messages (rate limits, quotas, auth errors) are now generally cleaned up before being shown to the user. No more raw JSON body in the chat UI — only the relevant core information.

Text insert chips cleared on chat switch

When switching to another chat, open "insert text" snippets (pasteBlocks) are automatically reset. No more accidental sending of text blocks copied from another chat.

Date display fix

Backend and frontend now consistently show message timestamps in the browser's local time zone. The fix affects chat history and project file lists.

---

ADDENDUM (2026-06-11): CLI Auth & Fallbacks

Auth modes per CLI provider

Selectable in the AI provider settings:

  • Automatic (recommended): OAuth preferred (free), API key as an

automatic fallback on error/limit.

  • OAuth only: no token costs, no fallback.
  • API key only: reliable, costs tokens.

Log in to Codex via OAuth (free, CLI 0.139+)

  1. In the OpenAI/ChatGPT account: Settings → Security → enable "Device code

authorization for Codex".

  1. In KnowSora: AI Provider → Codex → "Log in again".
  2. Copy the login URL, open it in the browser, log in with ChatGPT.
  3. The browser shows an error page (localhost:1455) — this is normal.
  4. Copy the complete address of this error page (with ?code=…) into

KnowSora's "Paste error page address" field → "Complete".

  1. Verify: docker exec knowsora_backend ls -la /data/.claude_home/.codex/auth.json

Claude Code via OAuth

AI Provider → Claude Code → "Log in again" → /login in the terminal → log in with the URL in the browser → paste the code back.

Repairing the CLI

On "exit 1" or hanging calls: "Repair" button at the respective CLI provider. Clears session/cache, token is preserved.

Complete auth documentation & changelog: see CHANGELOG-AUTH.md.

Setup Guide

Installation on any system — adjust ZIP names, port, and IP examples below for each product.

  1. Install Container Manager

DSM → Package Center → search for Container Manager and install.

  1. Enable SSH

DSM → Control Panel → Terminal & SNMP → enable SSH.

ssh admin@SYNOLOGY-IP
  1. Upload & unzip ZIP
scp productname_release.zip admin@SYNOLOGY-IP:/volume1/docker/
ssh admin@SYNOLOGY-IP
cd /volume1/docker
unzip productname_release.zip -d productname
cd productname
cp .env.example .env
  1. Start
./manage.sh update

WebUI: http://SYNOLOGY-IP:PORT — Login: admin / admin

💡 Synology DSM 7.2+ with Container Manager 20.10+ recommended. Adjust the path /volume1/docker/ if needed.
KnowSora

Price plus 19% VAT. One-time payment, no subscription costs, self-hosted.

  • 🔒 Encrypted API key storage (Fernet, AES-128 + HMAC)
  • 🧠 Contextual Retrieval (Anthropic method) for more precise KB match quality instead of simple chunk embedding
  • 🌐 Add web pages directly to the knowledge base via crawler
  • 📎 Images + files per chat message with vision support
  • 🎙️ Voice input/output (STT + TTS) via OpenAI or Gemini
  • 💾 Automatic download buttons for AI-generated files
  • 🎯 Project folder integration for CLI providers (direct file editing)
  • 🧩 Persistent user memory: AI remembers facts across chats and providers
  • 🔌 Skills system: create or import your own tools/skills
  • 📊 Usage dashboard with cost overview per provider
  • 🆓 OpenRouter integration including free models
  • 🔄 Background jobs for long requests (chat doesn't block)
  • 🌐 Embed via reverse proxy for your own subdomain possible
  • 👥 Multi-user with admin role and user management
Buy Now