GugleRAG is a self-hosted team knowledge base with Markdown documents, REST APIs, and an MCP JSON-RPC endpoint for AI agents.
| Service | Database |
|---|---|
![]() |
![]() |
| Retrieval | MCP |
|---|---|
![]() |
![]() |
| Login | Registration |
|---|---|
![]() |
![]() |
| Workspace | Create Document |
|---|---|
![]() |
![]() |
| Edit Document | Preview Document |
|---|---|
![]() |
![]() |
| Create Team | Join Team |
|---|---|
![]() |
![]() |
| Service Settings | User Management |
|---|---|
![]() |
![]() |
| MCP Configuration |
|---|
![]() |
.
├── src/
│ ├── api/ # REST handlers grouped by responsibility
│ ├── mcp/ # MCP JSON-RPC endpoint and tools
│ ├── auth.rs # JWT, password hashing, and account validation
│ ├── config.rs # Runtime/setup configuration
│ ├── db.rs # SQLx persistence
│ ├── desktop.rs # Windows desktop-launch system tray
│ ├── domain.rs # Shared domain models
│ ├── error.rs # HTTP-aware application errors
│ ├── embedding.rs # Embedding provider clients
│ ├── logging.rs # Rolling file and console logging
│ ├── reranker.rs # Optional reranking provider clients
│ ├── search.rs # Persistent vector retrieval and ranking
│ ├── lib.rs # Application composition
│ └── main.rs # Thin executable entry point
├── tests/ # Backend integration tests
├── frontend/ # Vue 3 + TypeScript + Vite frontend
├── PLAN.md # Product roadmap
└── AGENTS.md # Agent/developer working notes
The backend stores users, workspaces, teams, memberships, knowledge bases, documents, versions, invitations, and
document metadata through SQLx. Runtime configuration accepts SQLite, MySQL, and PostgreSQL DATABASE_URL values.
Active vector retrieval uses an embedded Rust HNSW index by default. Each knowledge base is persisted as one binary file
under VECTOR_INDEX_PATH (default data/vector-index), so the server remains a single executable without a separate
vector database service. Set VECTOR_DATABASE_URL to a PostgreSQL URL when the pgvector extension is available to
store and search vectors in a separate PostgreSQL database instead. SQL remains the source of truth for document content
and permissions; either vector backend is a rebuildable derived index.
The setup UI is implemented in Vue. The backend does not embed handwritten HTML.
Development flow:
cargo run
cd frontend
npm install
npm run devOpen the Vite URL, usually http://127.0.0.1:5173/. If .env does not exist, the Vue app shows a step-by-step setup
wizard and writes .env through /api/setup with:
SERVER_HOSTandSERVER_PORTDATABASE_URLfor SQLite, MySQL, or PostgreSQLJWT_SECRET- embedding, complete SiliconFlow endpoint, and model settings
- optional PostgreSQL
pgvectordatabase URL for vector storage - optional reranker settings
- MCP enablement and auth requirement
- optional
MCP_PUBLIC_URLfor reverse-proxy deployments
Restart the backend after saving .env.
Production/static flow:
cd frontend
npm install
npm run build
cd ..
cargo runThe backend serves frontend/dist as static files and falls back to frontend/dist/index.html for the Vue app.
The release workflow builds a non-root Debian Bookworm image for linux/amd64 and linux/arm64, then publishes a
combined manifest to ghcr.io/gu-zt/guglerag. Main-branch builds receive main, sha-<commit>, and immutable
v<version>-dev.<run> tags. Stable v<version> tags also publish v<version>, <version>, and latest.
docker run --rm -p 8080:8080 \
--env-file .env \
-v guglerag-data:/app/data \
-v guglerag-logs:/app/logs \
ghcr.io/gu-zt/guglerag:mainThe image includes the frontend build, stores SQLite data and vector indexes under /app/data, and writes logs under
/app/logs. GitHub Actions publishes it with the repository GITHUB_TOKEN; no additional registry secret is needed.
Double-clicking GugleRAG.exe in Windows Explorer starts it without a console window. After the HTTP server has
successfully bound its listener, GugleRAG creates a system-tray icon. Hovering over the icon shows the active listener
URL, and the right-click menu's Exit GugleRAG command stops the server gracefully. Launching through a terminal,
including cargo run, keeps the normal console-oriented behavior and does not create a tray icon.
cargo runBackend checks and tests:
cargo fmt -- --check
cargo check
cargo testThe server writes structured logs to both the console and logs/latest.log. At process startup, a non-empty previous
latest.log is compressed into logs/log-YY-MM-dd-HH:mm:ss:ms.log.gz and a new latest.log is created. The active
file rolls before a write would exceed 500 KiB. Windows does not allow colons in filenames, so Windows archives use
log-YY-MM-dd-HH-mm-ss-ms.log.gz instead.
Useful endpoints:
GET /healthGET /api/setup/statusPOST /api/setupPOST /api/auth/registerPOST /api/auth/loginGET/PUT /api/admin/config(administrator only)POST /api/admin/restart(administrator only)GET /api/workspacesGET/POST /api/workspaces/{workspace_id}/knowledge-basesGET/POST /api/teamsGET /api/teams/{team_id}/membersPOST /api/teams/{team_id}/invitationsGET /api/invitationsPOST /api/invitations/{token}/acceptGET /api/mcp/tokensDELETE /api/mcp/tokens/{token_id}GET/POST /api/documentsGET/PUT/DELETE /api/documents/{id}GET /api/search?q=...POST /mcpPOST /mcp/allPOST /mcp/{user|group}/{workspace_id}
Every user receives a personal workspace and its default knowledge base. Creating a team creates a team workspace and default knowledge base; team owners and admins can invite existing users by username. The invitation token can be shared with the invited user, who accepts it from the Join team dialog. A user may belong to multiple teams.
In the Vue workspace, use the selector at the top-left to switch between personal and team workspaces. Its adjacent +
menu contains team creation, member invitation, and team joining actions. The sidebar renders every knowledge base in
the selected workspace as a collapsible group with its articles nested underneath; new knowledge bases and articles can
be created directly from that tree.
Documents belong to a knowledge base. Document and search requests accept knowledge_base_id; when it is omitted, the
personal default knowledge base is used for backward compatibility.
The Vue workspace generates and copies MCP configurations through POST /api/mcp/configs. Each copy creates an
independent, scoped MCP access token instead of embedding the current login JWT. Personal and team configurations
identify a workspace explicitly:
{
"scope": "user",
"workspace_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"expires_in_days": 30
}Use scope: "group" with a team workspace_id, or scope: "all" without workspace_id for every workspace the
account can access. expires_in_days is optional and defaults to 30 days. The response uses the HTTP configuration
shape so clients can send the Authorization header:
{
"type": "http",
"url": "http://127.0.0.1:8080/mcp/user/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"headers": {
"Authorization": "Bearer ggr_..."
}
}The UUID at the end of a personal or group URL is the workspace ID, not an access token. The all-workspaces URL is
/mcp/all and has no trailing ID. The generated opaque token is stored only as a hash and is independent of the
login session. Every MCP request checks token expiry, revocation, the token's workspace scope, and current workspace
membership. GET /api/mcp/tokens lists token prefixes and metadata without exposing full secrets;
DELETE /api/mcp/tokens/{token_id} revokes one of the user's tokens. Set MCP_PUBLIC_URL when the server is behind a
public hostname or reverse proxy.
MCP clients can discover resources before operating on documents:
list_workspaces()returns the workspaces visible to the current MCP scope.list_knowledge_bases(workspace_id)returns the visible knowledge bases in that workspace.
Document read/write/list tools require one explicit workspace_id and knowledge_base_id; document-specific tools
additionally require their existing doc_id, folder_id, or content fields. search_knowledge accepts either one UUID
or an array of UUIDs for each resource parameter. Omit workspace_id to search every workspace visible to the current
MCP scope, omit knowledge_base_id to search every knowledge base in the selected workspaces, or omit both to search
every accessible knowledge base. Every explicit ID is still validated against the MCP scope and knowledge-base
ownership. Search results include workspace_id and knowledge_base_id so a result can be used with a document tool.
{
"name": "search_knowledge",
"arguments": {
"workspace_id": [
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
],
"knowledge_base_id": [
"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
],
"query": "deployment"
}
}Embedding and vector indexing are controlled by:
EMBEDDING_PROVIDER=stub|local|siliconflowEMBEDDING_MODEL=BAAI/bge-m3EMBEDDING_URL=https://api.siliconflow.cn/v1/embeddingsSILICONFLOW_URL=https://api.siliconflow.cnSILICONFLOW_API_KEY=sk-...for SiliconFlow embeddings or rerankingVECTOR_INDEX_PATH=data/vector-indexVECTOR_DATABASE_URL=postgresql://user:password@127.0.0.1:5432/vectors(optional)
The setup wizard defaults to SiliconFlow with BAAI/bge-m3. The default embedding request URL is the complete
https://api.siliconflow.cn/v1/embeddings endpoint; SILICONFLOW_URL remains the API base used to derive the reranker
endpoint. local uses the configured EMBEDDING_URL as an OpenAI-compatible HTTP endpoint. stub is a deterministic
offline provider intended for tests and installations that are not ready to call a model service.
Leave VECTOR_DATABASE_URL empty to use the embedded HNSW backend. When it is set, the target PostgreSQL database must
have the vector extension available; GugleRAG creates its vector table and filter/HNSW indexes on first use. The
vector database URL is independent of DATABASE_URL, so document metadata and vectors can be placed in different
databases. For every non-folder document, GugleRAG keeps the title and tags as context on overlapping content chunks. The chunk
window is selected from the configured model's documented input limit with a conservative character budget: 384
characters for the 512-token BGE-large/BCE models, 6,144 characters for the 8,192-token BGE-M3 models, and 8,192
characters for the 32,768-token Qwen3-Embedding models. Other models use a 4,000-character fallback. Chunk vectors,
text, content hashes, provider, and model are stored in the selected backend. Existing indexes or PostgreSQL rows are
reused when the document set, content hashes, provider, and model match; otherwise that knowledge base is rebuilt.
Search ranks a document by its best matching chunk and sends that chunk to the optional reranker. The server rebuilds
missing or stale indexes at startup; a first search also performs lazy indexing. SQL vectors from earlier versions are
automatically migrated into the selected backend when possible and regenerated when the old vector does not represent
the current chunk layout. Models above pgvector's HNSW dimension limit remain supported through PostgreSQL exact vector
search.
Reranking is optional and controlled by:
RERANKER_ENABLED=true|falseRERANKER_PROVIDER=local|siliconflow|custom_httpRERANKER_MODEL=BAAI/bge-reranker-v2-m3RERANKER_URL=http://...for local or custom HTTP reranker services
The SiliconFlow reranker uses SILICONFLOW_URL/v1/rerank. Local and custom HTTP rerankers use RERANKER_URL. Each
reranker receives { model, query, documents, top_n, return_documents: false } and may return results or
data entries containing index and score or relevance_score.
Markdown preview uses markdown-it with raw HTML disabled, followed by DOMPurify sanitization. It supports note/warning
containers and read-only GitHub-style task lists:
:::note Deployment details belong here.
:::
:::warning Optional title Check the production database before running this command.
:::
- [ ] Pending task
- [x] Completed taskTask checkboxes reflect the Markdown source and are intentionally disabled in preview mode.
cd frontend
npm install
npm run devThe Vite dev server proxies /api, /mcp, and /health to http://127.0.0.1:8080.
The current Vue workspace supports:
- user registration and login
- token persistence in local storage
- personal and team workspace switching
- multiple knowledge bases per workspace
- team creation, member lists, invitations, and invitation acceptance
- document list, create, edit, save, delete
- tag editing
- persistent embedding search over title, content, and tags, with optional model reranking
- edit/preview switching for Markdown text
Supported URL prefixes:
- SQLite:
sqlite://data/guglerag.db?mode=rwc - MySQL:
mysql://user:password@127.0.0.1:3306/guglerag - PostgreSQL:
postgresql://user:password@127.0.0.1:5432/guglerag
GitHub Actions validates the Rust backend, Vue frontend, release tooling, and a real HTTP server on every pull request
and push to main. Pull requests run CI without publishing. A successful main build publishes a prerelease after all
six native packages succeed. The supported release matrix is:
| Platform | Runner | Rust target | Archive |
|---|---|---|---|
| Linux x64 | ubuntu-24.04 |
x86_64-unknown-linux-gnu |
.tar.gz |
| Linux ARM64 | ubuntu-24.04-arm |
aarch64-unknown-linux-gnu |
.tar.gz |
| Windows x64 | windows-latest |
x86_64-pc-windows-msvc |
.zip |
| Windows ARM64 | windows-11-arm |
aarch64-pc-windows-msvc |
.zip |
| macOS Apple Silicon | macos-15 |
aarch64-apple-darwin |
.tar.gz |
| macOS Intel | macos-15-intel |
x86_64-apple-darwin |
.tar.gz |
Every target uses a matching native GitHub-hosted runner. CI parses each ELF, PE, or Mach-O header to verify the packaged CPU architecture and then starts that binary for a server smoke test.
Each archive is named guglerag-v<version>-<platform>-<arch>.<format> and has a matching .sha256 file. It contains
the server executable, frontend/dist, .env.example, both changelogs, this README, and RELEASE-METADATA.json. These
are unsigned portable builds and must be extracted before running.
Main-branch prereleases use v<manifest-version>-dev.<run_number>, for example v0.1.0-dev.42. A rerun keeps the same
GitHub run number and reuses the same release instead of creating a duplicate. The release remains a draft while
packages are uploading and becomes visible as a prerelease only after every target and the bilingual release notes
succeed.
To publish a stable release:
- Keep the version in
Cargo.toml,Cargo.lock,frontend/package.json, andfrontend/package-lock.jsonsynchronized. - Add matching
## [x.y.z]sections toCHANGELOG.mdandCHANGELOG.zh-CN.md. - Push the exact tag
vx.y.z.
The release workflow validates all six native targets, creates portable archives and checksums, generates bilingual
release notes, and publishes the draft only after every package succeeds. Stable releases use the exact manifest
version; prerelease artifacts append the CI build identifier while reading notes from the matching base-version
changelog section. The workflow uses the repository-provided GITHUB_TOKEN; no additional secrets or signing
credentials are required.














