Skip to content

RFC: headless Web/Server mode (--features server) + Connectors (Google Drive, local folder) - #729

Draft
johnsonafool wants to merge 5 commits into
nashsu:mainfrom
johnsonafool:web-mode
Draft

RFC: headless Web/Server mode (--features server) + Connectors (Google Drive, local folder)#729
johnsonafool wants to merge 5 commits into
nashsu:mainfrom
johnsonafool:web-mode

Conversation

@johnsonafool

@johnsonafool johnsonafool commented Sep 6, 2026

Copy link
Copy Markdown

Draft / RFC. This is a large change (≈90 files) and I'd like your steer before polishing it further. If the direction is welcome I can split it into three reviewable PRs (see the end). Everything here is opt-in behind a Cargo feature; the default desktop build is unchanged in behaviour.

What this adds

1. Headless Web / Server mode (--features server)

llm-wiki-server is a Tauri-free binary (axum) that serves the built React frontend over HTTP and answers everything the frontend used to get from Tauri IPC, so a small team can self-host one instance and use LLM Wiki from a browser.

browser ── /web/rpc/{cmd} ──▶ web/rpc.rs → the same command functions
        ◀─ /web/events  (SSE) ── AppCtx::emit
        ── /web/file, /web/upload, /web/proxy (outbound LLM calls), /web/auth/*
tiny_http :19828 local API + :19827 clip server run unchanged
  • AppCtx replaces tauri::AppHandle in commands; command bodies are untouched, only the attribute (#[cfg_attr(feature = "desktop", tauri::command)]) and the runtime shim (crate::rt::*) change.
  • Frontend: all host calls go through src/lib/backend/ (Tauri on desktop, /web/* in the browser); feature code no longer imports @tauri-apps/* directly. Plugin replacements (store, dialog, http, opener) are listed in plans/web-server-mode.md.
  • Auth: one shared password → HttpOnly; SameSite=Lax session cookie (or Authorization: Bearer). No password + loopback bind = open access (same trust model as the desktop); non-loopback bind without a password generates one and prints it.
  • Raw filesystem RPC commands are confined to registered project folders and the data dir (same allow-list as /web/file).
  • Dockerfile + docker-compose.yml; flags/env documented in plans/web-server-mode.md.

2. Connectors (Settings → Connectors)

Pull documents from external systems into raw/sources/@<name>/ on a schedule; the existing source-folder watcher then ingests them, so nothing downstream knows a connector exists.

  • Connector trait (test / list_all(cursor) / fetch), a descriptor registry that drives a generic settings form, a shared SyncEngine (version diff, sanitised paths, atomic writes, deletions, per-instance state), OAuth PKCE helper, minute scheduler.
  • Kinds shipped: local-folder (reference implementation) and google-drive (bring-your-own OAuth client, delta sync via changes.list, Docs/Sheets/Slides exported as Office files).
  • Design notes in plans/connectors.md.

3. Included fix (also submitted separately)

API / MCP chat now honours taskModelRouting.chatPresetId — see the standalone PR "fix(api): honor the Chat task preset for local API / MCP chat".

Verified

Check Result
npm run build (typecheck + vite)
vitest ✅ 132 files / 1876 tests
cargo check desktop and server features
cargo test --features server --lib 402 pass; 7 pre-existing Windows-only failures in commands::fs / commands::file_history (e.g. /tmp/... asserted absolute) — untouched by this change
Headless server smoke (login, RPC, file allow-list, upload, proxy, SSE, connectors, search, logout) ✅ 17/17
Desktop release build from this branch, run on Windows
Browser end-to-end (login → create project → import → ingest → chat with SSE) ✅ manually, Chromium

Not verified by me: macOS / Linux builds (CI should tell), long soak of ingest through /web/proxy.

Known gaps

  • Ingest still runs in the browser tab (as on desktop); closing the tab pauses it.
  • Trust model is one shared password; /web/proxy lets an authenticated user reach any URL from the server (documented; the desktop already allows pointing the app at any endpoint). TLS should sit in front for anything beyond a LAN.
  • Folder-import UX in the browser is upload-based; see "Known gaps / next milestones" in plans/web-server-mode.md.

Proposed split, if you'd like to take this

  1. AppCtx + rt host abstraction (mechanical, no behaviour change) — makes the rest possible and small to review.
  2. Web/Server mode behind the server feature.
  3. Connectors.

Happy to rebase, rename, or drop parts. Thanks for building LLM Wiki — we're using it as the engine for a team knowledge base and wanted to give this back.

johnsonafool and others added 5 commits September 5, 2026 16:55
Web / Server Mode
- headless `llm-wiki-server` binary (axum :8080) serving the built React
  frontend; frontend host calls go through src/lib/backend/ (Tauri IPC on
  desktop, /web/* JSON-RPC + SSE in the browser)
- AppCtx replaces tauri::AppHandle so command functions are shared by both
  builds; web/rpc.rs maps the commands to JSON-RPC
- shared-password cookie sessions, /web/file, /web/upload, /web/proxy,
  Dockerfile + docker-compose; plans/web-server-mode.md

Connectors
- Connector trait + registry, SyncEngine writing into raw/sources/@<name>/,
  per-instance state, OAuth PKCE, minute scheduler
- local-folder and google-drive kinds; Settings → Connectors UI generated
  from descriptors; plans/connectors.md

Cargo features: `desktop` (default) and `server`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…hat preset for API chat

Web mode
- The RPC bridge now applies the same project-folder / data-dir allow-list
  that `/web/file` already enforced to every raw filesystem command
  (read_file, write_file*, list_directory, copy_*, delete_file, file
  history, extraction, archive export ...). Before this, any authenticated
  web user could read or write arbitrary paths on the server through
  `/web/rpc/read_file` and friends.
- Targets that do not exist yet are checked via their nearest existing
  ancestor; `..` in the unresolved tail is rejected.
- Folders opened / created / imported through the bridge in this server
  session are accepted immediately: the persisted registry is written by
  the frontend only after `open_project` returns, so the first reads of a
  freshly opened project (and purely API-driven clients) used to be denied.

Local API / MCP chat
- `load_agent_runtime_config` now resolves the Chat task preset
  (`taskModelRouting.chatPresetId` + `providerConfigs`) the way the
  frontend's `resolveTaskLlmConfig` does, falling back to `llmConfig`.
  With Ingest on Claude Code CLI and Chat on Azure, API chats previously
  ignored the routing, hit the unsupported CLI provider and degraded to the
  retrieval-only answer.

Tests: unit tests for the path guard and the preset resolver; server-mode
smoke test (login, rpc, file, proxy, SSE, connectors, search, logout) 17/17.
Seven pre-existing `commands::fs` / `commands::file_history` unit tests
fail on Windows (upstream CI never runs `cargo test` on Windows; e.g.
`/tmp/...` is asserted to be absolute) — untouched by this change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant