# SiYuan Admin Guide

This guide is the operational and architectural reference for **SiYuan** — its internals, HTTP/WebSocket API, configuration surface, security model, deployment options, and operations. It is written for administrators, self-hosters, integrators, plugin authors, and developers. End-user feature walkthroughs live in the companion **User Guide**.

SiYuan is a privacy-first personal knowledge management system from **Yunnan Liandi Technology Co., Ltd. (B3log)**, licensed **AGPL-3.0**. It is composed of a Go **kernel** (the engine) and a TypeScript **UI** that ships as a desktop (Electron), mobile (Android/iOS/HarmonyOS), and browser front end. This guide is generated from the source at the commit noted in the footer.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [Repository Structure](#3-repository-structure)
4. [Requirements](#4-requirements)
5. [Boot Sequence and Process Model](#5-boot-sequence-and-process-model)
6. [Kernel Command-Line Reference](#6-kernel-command-line-reference)
7. [Environment Variables](#7-environment-variables)
8. [Configuration Reference (`conf.json`)](#8-configuration-reference-confjson)
9. [The HTTP/WebSocket Server](#9-the-httpwebsocket-server)
10. [Security Model](#10-security-model)
11. [HTTP API Reference](#11-http-api-reference)
12. [WebSocket Command Channel](#12-websocket-command-channel)
13. [WebDAV, CalDAV, and CardDAV](#13-webdav-caldav-and-carddav)
14. [Data Model and Storage](#14-data-model-and-storage)
15. [Synchronization and the Data Repo](#15-synchronization-and-the-data-repo)
16. [Deployment](#16-deployment)
17. [The Publish Service](#17-the-publish-service)
18. [Operations](#18-operations)
19. [Performance Tuning](#19-performance-tuning)
20. [Security Hardening Checklist](#20-security-hardening-checklist)
21. [Extensibility: Plugins, Widgets, and the Bazaar](#21-extensibility-plugins-widgets-and-the-bazaar)
22. [Code Quality and Conventions](#22-code-quality-and-conventions)
23. [Potential Improvements](#23-potential-improvements)
24. [Troubleshooting](#24-troubleshooting)
25. [Quick Reference](#25-quick-reference)

---

## 1. Executive Summary

SiYuan's defining architectural decision is the **kernel/UI split**. The kernel is a single Go binary that runs a local HTTP + WebSocket server (gin + melody) on port `6806`. It owns all state: it parses and stores block-structured Markdown (via the **Lute** engine), maintains a SQLite block index, renders databases, runs spaced-repetition scheduling (**riff**/FSRS), performs encrypted versioned sync (**dejavu**), and exposes ~485 JSON endpoints. The UI — whether the Electron desktop shell, a mobile app, or a plain browser — is a thin client that talks to that local kernel over the same API.

This design yields three deployment shapes from one codebase:

| Shape | Kernel | UI | Notes |
|---|---|---|---|
| **Desktop** | Bundled, child process of Electron | Electron renderer (`/stage/build/{app,desktop}`) | Full feature set incl. PDF/Word/HTML export |
| **Mobile** | Compiled into the app (`gomobile`) | Native shell + web view (`/stage/build/mobile`) | Android/iOS/HarmonyOS |
| **Self-hosted** | `b3log/siyuan` Docker image | Any browser over `:6806` | Browser only; no desktop/mobile connect; no PDF/Word/HTML export, no MD import |

| Attribute | Value |
|---|---|
| License | AGPL-3.0 |
| Vendor | Yunnan Liandi Technology Co., Ltd. (B3log) |
| Kernel language | Go (see `go.mod` for the toolchain version) |
| UI language | TypeScript + webpack; Electron desktop shell |
| Default kernel port | `6806` (HTTP/WS); publish service `6808` |
| Primary data store | Per-document `.sy` JSON files + SQLite block index |
| Sync | Encrypted snapshots to SiYuan Cloud / S3 / WebDAV / local FS |

---

## 2. System Architecture

```
                    ┌──────────────────────────────────────────────┐
                    │  Front ends (thin clients)                    │
                    │  Electron desktop · Android/iOS/HarmonyOS ·   │
                    │  Browser (self-hosted)                        │
                    └───────────────┬──────────────────────────────┘
                                    │ HTTP (JSON, POST /api/*)
                                    │ WebSocket (/ws, melody) — realtime push & commands
                                    ▼
   ┌───────────────────────────────────────────────────────────────────────┐
   │  Kernel (Go, single binary)                                            │
   │                                                                         │
   │  gin HTTP engine  ──► middleware: ControlConcurrency → Timing →         │
   │                       Recover → CORS → JWT → gzip → sessions            │
   │                                                                         │
   │  kernel/api      REST handlers (~485 endpoints, /api/{domain}/{action}) │
   │  kernel/server   HTTP/WS wiring, WebDAV/CalDAV/CardDAV, static serving  │
   │  kernel/model    Business logic (the largest package)                  │
   │  kernel/cmd      WebSocket command dispatch                            │
   │  kernel/sql      SQLite block / history / asset-content indexes        │
   │  kernel/av       Attribute views (databases)                          │
   │  kernel/treenode Block tree + block-tree index                        │
   │  kernel/filesys  .sy document (de)serialization                       │
   │  kernel/bazaar   Marketplace (plugins/widgets/themes/icons/templates) │
   │  kernel/conf     Configuration structs (conf.json)                    │
   │  kernel/job      Cron jobs (history, sync, cleanup)                    │
   │  kernel/task     Background task queue                                 │
   │  kernel/util     Boot, paths, TLS, OCR, pandoc, device, working dir   │
   │  kernel/cache    In-memory caches (assets, etc.)                      │
   │  kernel/mobile   gomobile entry points for mobile apps                │
   │  kernel/harmony  HarmonyOS-specific glue                              │
   └───────────────────────────────┬───────────────────────────────────────┘
                                    │ uses
                                    ▼
   Lute (Markdown engine) · dejavu (data repo) · riff/go-fsrs (flashcards) ·
   go-sqlite3 (index) · pdfium/pdfcpu (PDF) · gosseract (OCR) · go-openai (AI) ·
   aws-sdk-go-v2/s3 · gowebdav/go-webdav (sync, CalDAV/CardDAV) · excelize (xlsx)
```

### 2.1 Request lifecycle (REST)

1. A client sends `POST /api/{domain}/{action}` with a JSON body.
2. gin runs the global middleware chain: **ControlConcurrency** (serializes kernel API requests), **Timing**, **Recover**, **CORS**, **JWT** (parses the `X-Auth-Token` and sets the request role), **gzip**, and **sessions**.
3. Per-route middleware enforces auth: `CheckAuth` → optionally `CheckAdminRole` → optionally `CheckReadonly`.
4. The handler in `kernel/api/*.go` validates the request, calls into `kernel/model`, and returns a JSON envelope `{code, msg, data}`.

### 2.2 Realtime channel

The UI maintains a WebSocket to `/ws` (melody). The kernel **pushes** progress, notifications, and UI-reload events to clients, and also accepts **commands** over the socket (parsed into `cmd.NewCommand` and run via `cmd.Exec`). This is how long-running operations (sync, indexing, transactions) report progress live.

### 2.3 One kernel, three UIs

The root route (`/`) inspects the `User-Agent` and redirects to the correct static bundle: `Electron` → `/stage/build/app/`, tablets/`Pad`/non-mobile Android → `/stage/build/desktop/`, mobile → `/stage/build/mobile/`, otherwise desktop. All three bundles are produced from the `app/` TypeScript source via separate webpack configs.

---

## 3. Repository Structure

The repo is a polyglot monorepo: `app/` (TypeScript front end) + `kernel/` (Go back end).

| Path | Purpose |
|---|---|
| `kernel/main.go` | Desktop/server kernel entry point (boot sequence) |
| `kernel/api/` | REST handlers + `router.go` (the authoritative endpoint list) |
| `kernel/server/` | HTTP/WS server, WebDAV/CalDAV/CardDAV, static serving, proxy |
| `kernel/model/` | Core business logic (largest package): documents, blocks, sync, export, import, history, flashcards, attribute views, etc. |
| `kernel/cmd/` | WebSocket command registry and executor |
| `kernel/sql/` | SQLite schema and queries (block, history, asset-content DBs) |
| `kernel/av/` | Attribute views (databases) |
| `kernel/treenode/` | Block tree and block-tree index |
| `kernel/filesys/` | `.sy` document read/write |
| `kernel/bazaar/` | Marketplace client (install/stage/readme) |
| `kernel/conf/` | Configuration structs (one struct per concern) |
| `kernel/job/`, `kernel/task/` | Cron scheduler and background task queue |
| `kernel/util/` | Boot, path resolution, TLS certs, OCR, pandoc, device id, working dir |
| `kernel/cache/` | In-memory caches |
| `kernel/mobile/`, `kernel/harmony/` | Mobile (`gomobile`) and HarmonyOS entry points |
| `app/electron/main.js` | Electron main process (window/workspace/tray/kernel-child management) |
| `app/src/` | UI source: `editor/`, `protyle/` (editor core), `layout/`, `menus/`, `dialog/`, `config/`, `search/`, `plugin/`, `mobile/`, `window/`, `asset/`, `emoji/` |
| `app/stage/` | Static runtime assets and vendored renderers (KaTeX, Mermaid, ECharts, PDF.js, highlight.js, PlantUML, Graphviz, abcjs) |
| `app/appearance/` | Themes, icons, emojis, language packs (`langs/`) |
| `app/pandoc/` | Bundled Pandoc resources |
| `app/changelogs/`, `app/guide/` | Release changelogs and the built-in user-guide notebook |
| `scripts/` | Build/packaging helpers |

> [!NOTE]
> `app/changelogs/` and `app/guide/` are large by file count but are content, not code — the guide folders are SiYuan notebooks (`.sy` documents with ID-named subfolders), not source to be deployed as logic.

---

## 4. Requirements

### 4.1 Runtime (end users)

| Deployment | Requirement |
|---|---|
| Desktop | Windows / macOS / Linux (x64 and arm64 builds available) |
| Mobile | Android / iOS / iPadOS / HarmonyOS |
| Self-hosted | Docker host; a modern browser to access the UI |

The desktop app bundles the kernel, Electron runtime, and Pandoc — no separate installs needed.

### 4.2 Build / development

| Component | Toolchain |
|---|---|
| Kernel | Go (version pinned in `kernel/go.mod`); CGO for SQLite |
| UI | Node.js + `pnpm` (version pinned in `app/package.json` `packageManager`); webpack; TypeScript |
| Desktop packaging | `electron` + `electron-builder` (per-platform/arch `electron-builder*.yml` configs) |
| Export | Pandoc (bundled for the desktop) |
| OCR | Tesseract (bundled/available for OCR features) |

UI build scripts (`app/package.json`):

```bash
pnpm run dev            # webpack dev build (app)
pnpm run dev:desktop    # dev build, desktop bundle
pnpm run dev:mobile     # dev build, mobile bundle
pnpm run build          # production build of all bundles
pnpm run dist           # electron-builder package (current platform)
pnpm run dist-darwin    # macOS package, etc. (see all dist-* scripts)
```

> [!NOTE]
> The kernel uses a **forked SQLite driver** (`88250/go-sqlite3`) and a **forked pdfcpu** (`88250/pdfcpu`) via `replace` directives in `go.mod`. Build from the module graph as-is; don't substitute upstream versions without testing.

---

## 5. Boot Sequence and Process Model

`kernel/main.go` defines the startup order (desktop/server build; the `//go:build !mobile` tag separates it from mobile entry points):

```go
util.Boot()                       // parse flags, resolve workspace/paths, logging, lock
model.InitConf()                  // load conf.json (defaults via conf.NewX())
go server.Serve(false, CookieKey) // start gin HTTP/WS server (async)
model.InitAppearance()            // themes, icons, languages
sql.InitDatabase(false)           // main block index (SQLite)
sql.InitHistoryDatabase(false)    // history index
sql.InitAssetContentDatabase(false) // asset-content (search) index
model.BootSyncData()              // boot-time sync if enabled
model.InitBoxes()                 // open notebooks
model.LoadFlashcards()            // FSRS decks
job.StartCron()                   // scheduled jobs
... watchers (assets, emojis, themes) + HandleSignal()
```

Key facts:

- **Workspace lock**: only one kernel may use a workspace at a time (the "workspace is locked" error indicates a second kernel).
- **Port file**: the kernel writes its chosen port keyed by PID to `~/.config/siyuan/port.json` so launchers and tools can discover it.
- **Desktop child process**: the Electron main process (`app/electron/main.js`) launches the kernel as a child with flags `--port`, `--wd <appDir>`, `--workspace`, `--lang`, and (in dev) `--mode dev`. It manages multiple workspaces/windows, a system tray per workspace, single-instance locking, and the `siyuan://` protocol handler.

> [!IMPORTANT]
> On desktop you never start the kernel yourself — Electron does. For self-hosting you run the kernel binary directly (the Docker entrypoint does this for you), so the CLI flags below matter.

---

## 6. Kernel Command-Line Reference

Flags parsed in `kernel/util/working.go`:

| Flag | Default | Purpose |
|---|---|---|
| `--workspace` | `~/SiYuan/` | Workspace directory path |
| `--wd` | (binary working dir) | Working directory of SiYuan (where `stage/`, `appearance/`, etc. live) |
| `--port` | `0` | HTTP server port (`0` = pick a free port; conventionally `6806`) |
| `--readonly` | `false` | Start in read-only mode (blocks all write operations) |
| `--accessAuthCode` | `""` | Access authorization code (the kernel's password) |
| `--ssl` | `false` | Enable HTTPS/WSS |
| `--lang` | `""` | Interface language (e.g. `en_US`, `zh_CN`, `ja_JP`, `de_DE`, `es_ES`, `fr_FR`, `ko_KR`, `ru_RU`, `pt_BR`, `tr_TR`, `zh_CHT`, …) |
| `--mode` | `prod` | `dev` or `prod` (dev enables `/debug/pprof` and dev asset paths) |

The Docker entrypoint additionally honors `--workspace=` and forwards any extra arguments to the kernel.

```bash
# Run the kernel directly (self-host without Docker)
./kernel --workspace=/data/siyuan --port=6806 --accessAuthCode=please-change-me --lang=en_US
```

---

## 7. Environment Variables

The kernel reads these via `os.Getenv` (UI/Electron reads `NODE_ENV`):

| Variable | Used by | Effect |
|---|---|---|
| `SIYUAN_WORKSPACE_PATH` | Docker entrypoint | Workspace path (CLI `--workspace` wins if both set) |
| `SIYUAN_ACCESS_AUTH_CODE` | Docker entrypoint | Access auth code (CLI `--accessAuthCode` wins) |
| `SIYUAN_ACCESS_AUTH_CODE_BYPASS` | `kernel/util/working.go` | If `true`, bypasses the access auth code (insecure; use only in trusted/dev contexts) |
| `SIYUAN_OPENAI_API_KEY` | `kernel/conf/ai.go` | Default OpenAI API key |
| `SIYUAN_OPENAI_API_TIMEOUT` | `kernel/conf/ai.go` | OpenAI request timeout (seconds; default 30) |
| `SIYUAN_OPENAI_API_PROXY` | `kernel/conf/ai.go` | Proxy for OpenAI calls |
| `SIYUAN_OPENAI_API_MAX_TOKENS` | `kernel/conf/ai.go` | Max tokens |
| `SIYUAN_OPENAI_API_TEMPERATURE` | `kernel/conf/ai.go` | Sampling temperature (default 1.0) |
| `SIYUAN_OPENAI_API_MAX_CONTEXTS` | `kernel/conf/ai.go` | Number of context messages (default 7) |
| `SIYUAN_OPENAI_API_BASE_URL` | `kernel/conf/ai.go` | API base URL (default `https://api.openai.com/v1`) |
| `SIYUAN_OPENAI_API_USER_AGENT` | `kernel/conf/ai.go` | Custom user agent |
| `SIYUAN_TESSERACT_ENABLED` | `kernel/util/ocr.go` | Enable Tesseract OCR |
| `SIYUAN_TESSERACT_TIMEOUT` | `kernel/util/ocr.go` | OCR timeout |
| `SIYUAN_TESSERACT_MAX_SIZE` | `kernel/util/ocr.go` | Max image size for OCR |
| `SIYUAN_TESSERACT_LANGS` | `kernel/util/ocr.go` | OCR languages |
| `SIYUAN_PDF_ASSET_CONTENT_INDEX_MAX_SIZE` | `kernel/model/asset_content.go` | Max PDF size to index for asset-content search |
| `SIYUAN_SYNC_INDEX_TIMING` | `kernel/model/conf.go` | Sync-index timing threshold |
| `SIYUAN_PERFORMANCE_TIMING` | `kernel/model/session.go` | Enable performance timing logs |
| `PUID` / `PGID` | Docker entrypoint | Run-as user/group IDs inside the container (default 1000/1000) |
| `NODE_ENV` | `app/electron/main.js` | Electron dev/prod mode |
| `APPDATA` / `ProgramData` / `USERPROFILE` | `kernel/util/path.go`, `working.go` | Windows path resolution |

> [!CAUTION]
> `SIYUAN_ACCESS_AUTH_CODE_BYPASS=true` disables authentication entirely. Never set it on an instance reachable by anyone but you.

---

## 8. Configuration Reference (`conf.json`)

Runtime configuration lives in `workspace/conf/conf.json`, deserialized into the `AppConf` aggregate. Each concern is its own struct in `kernel/conf/*.go`, with JSON keys and defaults defined by the corresponding `NewX()` constructor. Per-notebook settings live in each notebook's `.siyuan/conf.json` (`BoxConf`).

> [!NOTE]
> Most settings are managed through the in-app **Settings** UI (which calls the `/api/setting/*` and `/api/system/*` endpoints). Editing `conf.json` by hand is possible but should be done with the kernel stopped.

### 8.1 `System` (`kernel/conf/system.go`)

| Key | Type | Notes |
|---|---|---|
| `id`, `name` | string | Device ID and name |
| `kernelVersion`, `os`, `osPlatform` | string | Runtime info |
| `container` | string | `docker` / `android` / `ios` / `harmony` / `std` |
| `isMicrosoftStore`, `isInsider` | bool | Build flavor |
| `homeDir`, `workspaceDir`, `appDir`, `confDir`, `dataDir` | string | Resolved paths |
| `networkServe` | bool | Bind to `0.0.0.0` (LAN serving) instead of `127.0.0.1` |
| `networkServeTLS` | bool | Enable HTTPS (requires `networkServe`) |
| `networkProxy` | object | `{scheme, host, port}` outbound proxy |
| `downloadInstallPkg` | bool | Auto-download update packages |
| `autoLaunch2` | int | 0 off / 1 on / 2 on+hidden |
| `lockScreenMode` | int | 0 manual / 1 manual+follow-system |
| `disabledFeatures` | []string | Feature flags to disable |
| `microsoftDefenderExcluded` | bool | Whether the workspace was added to Defender exclusions |

### 8.2 `Editor` (`kernel/conf/editor.go`) — selected keys

| Key | Default | Notes |
|---|---|---|
| `fontSize` | 16 | |
| `allowSVGScript`, `allowHTMLBLockScript` | false | Execute scripts inside SVG/HTML blocks (security-sensitive) |
| `codeSyntaxHighlightLineNum`, `codeLineWrap`, `codeLigatures` | false | Code-block rendering |
| `codeTabSpaces` | 0 | Tab→spaces in code (0 = off) |
| `generateHistoryInterval` | 10 | File-history interval (minutes) |
| `historyRetentionDays` | 30 | History retention |
| `virtualBlockRef` | false | Virtual references; `virtualBlockRefInclude/Exclude` keyword lists |
| `blockRefDynamicAnchorTextMaxLen` | 96 | |
| `plantUMLServePath` | `https://www.plantuml.com/plantuml/svg/~1` | PlantUML render server |
| `fullWidth` | true | Max-width layout |
| `katexMacros` | `{}` | KaTeX macro definitions |
| `readOnly` | false | Editor read-only |
| `dynamicLoadBlocks` | 192 | Blocks loaded per chunk (range 48–1024) |
| `spellcheck` / `spellcheckLanguages` | false / `["en-US"]` | |
| `backlinkExpandCount` | 8 | |
| `floatWindowMode` / `floatWindowDelay` | 0 / 620ms | Hover-preview behavior |
| `markdown` | object | Markdown parsing settings |

### 8.3 `Appearance` (`kernel/conf/appearance.go`)

| Key | Default | Notes |
|---|---|---|
| `mode` / `modeOS` | 0 (light) / true | Theme mode; follow OS |
| `themeDark` / `themeLight` | `midnight` / `daylight` | |
| `codeBlockThemeDark` / `codeBlockThemeLight` | `base16/dracula` / `github` | |
| `icon` | `material` | Icon set |
| `lang` | `en_US` | UI language |
| `closeButtonBehavior` | 0 | 0 quit / 1 minimize to tray |
| `hideStatusBar` | false | |

### 8.4 `Sync` (`kernel/conf/sync.go`)

| Key | Default | Notes |
|---|---|---|
| `enabled` | false | Master sync toggle |
| `cloudName` | `main` | Cloud sync directory name |
| `mode` | 1 | 1 auto / 2 manual / 3 fully manual |
| `interval` | 30 | Auto-sync seconds |
| `provider` | 0 | 0 SiYuan / 2 S3 / 3 WebDAV / 4 Local |
| `perception` | false | Sync awareness |
| `generateConflictDoc` | false | Create a conflict doc on conflict |
| `s3` | object | `endpoint, accessKey, secretKey, bucket, region, pathStyle, skipTlsVerify, timeout, concurrentReqs` |
| `webdav` | object | `endpoint, username, password, skipTlsVerify, timeout, concurrentReqs` |
| `local` | object | `endpoint` (local dir), `timeout`, `concurrentReqs` |

### 8.5 `Repo` (`kernel/conf/repo.go`)

| Key | Default | Notes |
|---|---|---|
| `key` | — | AES key for the encrypted data repo (binary) |
| `syncIndexTiming` | 12000 | ms threshold to warn about slow indexing |
| `indexRetentionDays` | 180 | Auto-purge local indexes older than this |
| `retentionIndexesDaily` | 2 | Indexes kept per day |

### 8.6 `Publish` (`kernel/conf/publish.go`)

| Key | Default | Notes |
|---|---|---|
| `enable` | false | Enable the publish service |
| `port` | 6808 | Publish service port |
| `auth.enable` | true | Basic auth on the publish service |
| `auth.accounts[]` | [] | `{username, password, memo}` accounts |

### 8.7 Other config structs

| Struct | File | Highlights |
|---|---|---|
| `AI` / `OpenAI` | `ai.go` | API key, base URL, model, provider (OpenAI/Azure), temperature 1.0, contexts 7, timeout 30 |
| `API` | `api.go` | `token` (16-char API token, randomly generated) |
| `Search` | `search.go` | Per-block-type search toggles, `limit` 64, `caseSensitive`, name/alias/memo/IAL flags, asset-path indexing, backlink/virtual-ref matching |
| `Export` | `export.go` | Block-ref/embed modes, PDF footer `%page / %pages`, watermarks, Pandoc bin/params, YAML front matter, include sub/related docs |
| `Flashcard` | `flashcard.go` | New/review limits 20/200, card-making modes, FSRS `requestRetention`/`maximumInterval`/`weights` |
| `Graph` | `graph.go` | `maxBlocks` 10240, local/global graph type filters and D3 force params |
| `FileTree` | `filetree.go` | Sort, `maxListCount` 512, `maxOpenTabCount` 8, deep-create guard, large-file warning |
| `Bazaar` | `bazaar.go` | `trust`, `petalDisabled` (disable plugin system) |
| `Tag` | `tag.go` | Sort mode |
| `Snippet`/`Snpt` | `snippet.go` | `enabledCSS`, `enabledJS`; per-snippet `{id,name,type,enabled,disabledInPublish,content}` |
| `Account`, `User`, `Stat` | `account.go`, `user.go`, `stat.go` | Cloud account display, membership/traffic fields, content statistics |
| `BoxConf` | `box.go` | Per-notebook: name, icon, sort, create/daily-note paths + template |

---

## 9. The HTTP/WebSocket Server

Defined in `kernel/server/serve.go` (`Serve(fastMode, cookieKey)`).

### 9.1 Binding and ports

```go
if model.Conf.System.NetworkServe || util.ContainerDocker == util.Container {
    host = "0.0.0.0"   // LAN-accessible (or always in Docker)
} else {
    host = "127.0.0.1" // localhost only (default)
}
```

- Default bind is **loopback only**. Setting `networkServe` (or running in Docker) binds to all interfaces.
- The actual port is recorded to `~/.config/siyuan/port.json` keyed by PID.
- HTTP/2 cleartext (`UseH2C`) is enabled; max multipart upload memory is 32 MiB.

### 9.2 TLS

When `networkServeTLS` **and** `networkServe` are both on, the kernel obtains/creates a self-signed CA + cert (`util.GetOrCreateTLSCert`) and serves HTTPS, multiplexing TLS and plain connections on a fixed port via `cmux`. You can export the CA cert/bundle via API (`/api/system/exportTLSCACert`, `exportTLSCABundle`) and import a bundle.

### 9.3 Middleware chain (global)

`ControlConcurrency` (serialize kernel API requests — see [issue #9939]) → `Timing` → `Recover` → `corsMiddleware` → `jwtMiddleware` → `gzip` (excluding already-compressed media) → `sessions` (cookie store named `siyuan`, `HttpOnly`, `Secure` when SSL).

### 9.4 Static and special route groups

| Route | Auth | Serves |
|---|---|---|
| `/` | CheckAuth | Redirects to `/stage/build/{app,desktop,mobile}` by User-Agent |
| `/stage/*` | CheckAuth | UI bundles and static assets |
| `/appearance/*filepath` | CheckAuth | Themes/icons/langs (fills missing lang keys from `en_US`) |
| `/assets/*path` | CheckAuth | Workspace assets (path-escaped, publish-access-checked, thumbnail/SVG handling) |
| `/history/*path` | CheckAuth + Admin | File history |
| `/repo/diff/*path` | CheckAuth + Admin | Repo snapshot diffs |
| `/export/*filepath` | CheckAuth | Export downloads (sub-path + sensitive-file guarded; forces attachment download) |
| `/widgets/`, `/plugins/`, `/emojis/`, `/templates/` | CheckAuth | Static dirs under `data/` |
| `/public/` | (open) | `data/public/` contents |
| `/snippets/*filepath` | CheckAuth | Snippet content (`conf.json` requires admin role) |
| `/upload` | CheckAuth + Admin + ¬Readonly | File upload |
| `/check-auth` | (open) | Auth page (HTML) |
| `/ws` | (handshake auth) | WebSocket |
| `/webdav/*`, `/caldav/*`, `/carddav/*`, `/.well-known/{caldav,carddav}` | CheckAuth + Admin | WebDAV/CalDAV/CardDAV |
| `/debug/pprof/*` | dev mode only | Go profiling |
| `favicon.ico`, `manifest.json`, `service-worker.js` | (open) | PWA fixed files |

> [!WARNING]
> `/debug/pprof/*` is registered **only** when `--mode dev`. Production builds do not expose pprof (per [issue #10152]).

### 9.5 CORS

The CORS middleware sets `Access-Control-Allow-Origin: *` with credentials allowed, and tailors `Access-Control-Allow-Methods` per route family (standard HTTP, WebDAV, CalDAV, CardDAV methods). `OPTIONS` preflight returns 204 with a 600-second max-age.

> [!CAUTION]
> `Access-Control-Allow-Origin: *` combined with `Allow-Credentials: true` means any web origin can attempt authenticated requests **if it has valid credentials/token**. Authentication is enforced by the auth code / token, not by origin. Treat the access auth code and API token as secrets and never expose the kernel directly to the internet without a reverse proxy and strong auth.

---

## 10. Security Model

SiYuan's authorization is enforced by three composable gin middlewares plus a role system. Defined in `kernel/model` and applied in `kernel/api/router.go` and `kernel/server/serve.go`.

### 10.1 Authentication layers

1. **Access Auth Code** — the workspace "password" (`Conf.AccessAuthCode`). When set, clients must authenticate (via the `/check-auth` page → session cookie). Can be supplied at launch via `--accessAuthCode` / `SIYUAN_ACCESS_AUTH_CODE`, or bypassed (insecurely) with `SIYUAN_ACCESS_AUTH_CODE_BYPASS=true`.
2. **API Token** — `Conf.API.Token` (a random 16-char token). Sent as `X-Auth-Token` for programmatic/API access; parsed by `jwtMiddleware`.
3. **Session cookie** — gin cookie session (`siyuan`), `HttpOnly`, `Secure` under TLS.

### 10.2 Roles

The JWT/token carries a **role**; `jwtMiddleware` sets it on the request (defaulting to `Visitor` when no valid token):

| Role | Capability |
|---|---|
| `Administrator` | Full read/write/admin |
| `Editor` | Read/write |
| `Reader` | Read-only |
| `Visitor` | Minimal/unauthenticated |

### 10.3 Authorization middleware

| Middleware | Enforces |
|---|---|
| `CheckAuth` | A valid session or token (authenticated) |
| `CheckAdminRole` | The request role is administrator (write/admin endpoints) |
| `CheckReadonly` | The kernel is **not** in read-only mode (blocks mutations when `--readonly` or `Editor.ReadOnly`) |

A typical mutating endpoint stacks all three: `CheckAuth, CheckAdminRole, CheckReadonly`. Read endpoints often use only `CheckAuth`. A handful of bootstrap endpoints (`bootProgress`, `version`, `currentTime`, `loginAuth`, `logoutAuth`, `getCaptcha`, `getDynamicIcon`) require **no** auth.

### 10.4 Publish-access checks

When an asset is requested by a non-admin context, the server consults the document's **publish access** settings (`GetPublishAccess` / `CheckAbsPathAccessableByPublishAccess`) before serving — so published/shared documents only expose what they're allowed to.

### 10.5 Content-execution safeguards

- **SVG sanitization**: SVG assets are sanitized unless `Editor.AllowSVGScript` is enabled.
- **HTML block scripts**: disabled unless `Editor.AllowHTMLBLockScript` is on.
- **Sensitive paths**: the export server refuses to serve files it deems sensitive and enforces sub-path containment to prevent path traversal.
- **IP allow/deny**: the network forward-proxy endpoint rejects prohibited IPs (`ip address [%s] is prohibited`) to mitigate SSRF.
- **CA/JWT**: TLS CA management and JWT parsing live in `kernel/util/cert.go` and `kernel/model` respectively.

> [!IMPORTANT]
> The single most important hardening step for any networked instance is a **strong access auth code** plus a reverse proxy terminating TLS. See the [hardening checklist](#20-security-hardening-checklist).

---

## 11. HTTP API Reference

The kernel exposes a JSON REST API registered in `kernel/api/router.go`. **All endpoints accept `POST`** (a few also `GET`) at `/api/{domain}/{action}` and return `{code, msg, data}`. Authenticate with the API token via the `X-Auth-Token` header. There are ~485 endpoints; they are grouped by domain below. The official endpoint catalog is also published as [API.md](https://github.com/siyuan-note/siyuan/blob/master/API.md).

**Auth legend:** 🔓 no auth · 🔑 CheckAuth · 🛡 +Admin role · 🔒 +Readonly guard (mutating).

### 11.1 Calling convention

```bash
curl -X POST http://127.0.0.1:6806/api/notebook/lsNotebooks \
  -H "Authorization: Token <your-api-token>" \
  -H "X-Auth-Token: <your-api-token>" \
  -H "Content-Type: application/json" \
  -d '{}'
```

> [!NOTE]
> Request/response bodies vary per endpoint; consult `kernel/api/*.go` handlers (or API.md) for exact fields. The tables below are the authoritative endpoint inventory grouped by function.

### 11.2 System & lifecycle (`/api/system/*`)

| Endpoint | Auth | Purpose |
|---|---|---|
| `bootProgress` (GET/POST) | 🔓 | Boot progress |
| `version` (GET/POST), `currentTime` | 🔓 | Kernel version / server time |
| `getConf` | 🔑 | Full configuration |
| `getNetwork`, `getSysFonts`, `getChangelog`, `getWorkspaceInfo` | 🔑/🛡 | System info |
| `setNetworkServe`, `setNetworkServeTLS`, `setNetworkProxy` | 🛡🔒 | Network serving / TLS / proxy |
| `exportTLSCACert`, `exportTLSCABundle`, `importTLSCABundle` | 🛡(🔒) | TLS CA management |
| `setAccessAuthCode`, `setFollowSystemLockScreen`, `setAPIToken` | 🛡🔒 | Auth settings |
| `setWorkspaceDir`, `getWorkspaces`, `getMobileWorkspaces`, `checkWorkspaceDir`, `createWorkspaceDir`, `removeWorkspaceDir`, `removeWorkspaceDirPhysically` | 🛡(🔒) | Workspace management |
| `setAppearanceMode`, `setUILayout` | 🛡🔒 | UI/appearance |
| `setAutoLaunch`, `setDownloadInstallPkg`, `checkUpdate` | 🛡(🔒) | Launch & updates |
| `exportConf`, `importConf`, `exportLog` | 🛡(🔒) | Config import/export, logs |
| `vacuumDataIndex`, `rebuildDataIndex`, `clearTempFiles` | 🛡🔒 | Index/temp maintenance |
| `addMicrosoftDefenderExclusion`, `ignoreAddMicrosoftDefenderExclusion` | 🛡🔒 | Windows Defender |
| `exit`, `reloadUI` (deprecated→`/api/ui/reloadUI`) | 🛡 | Lifecycle |

### 11.3 Notebooks (`/api/notebook/*`)

`lsNotebooks` 🔑 · `openNotebook` `closeNotebook` `createNotebook` `removeNotebook` `renameNotebook` `changeSortNotebook` `setNotebookIcon` 🛡🔒 · `getNotebookConf` `getNotebookInfo` 🔑 · `setNotebookConf` 🛡🔒.

### 11.4 File tree / documents (`/api/filetree/*`)

| Group | Endpoints |
|---|---|
| Read 🔑 | `searchDocs`, `listDocsByPath`, `getDoc`, `getDocCreateSavePath`, `getRefCreateSavePath`, `getHPathByPath`, `getHPathsByPaths`, `getHPathByID`, `getPathByID`, `getFullHPathByID`, `getIDsByHPath` |
| Create/modify 🛡🔒 | `createDoc`, `createDocWithMd`, `createDailyNote`, `renameDoc`, `renameDocByID`, `removeDoc`, `removeDocByID`, `removeDocs`, `moveDocs`, `moveDocsByID`, `duplicateDoc`, `changeSort` |
| Restructure 🛡🔒 | `doc2Heading`, `heading2Doc`, `li2Doc` |
| Index/publish 🛡🔒 | `upsertIndexes`, `removeIndexes`, `listDocTree`, `moveLocalShorthands`, `setPublishAccess`, `getPublishAccess`, `authFilePublishAccess` |

### 11.5 Blocks (`/api/block/*`)

The richest domain. Read operations (🔑/🛡): `getBlockInfo`, `getBlockDOM(s)`, `getBlockDOM(s)WithEmbed`, `getBlockKramdown(s)`, `getChildBlocks`, `getTailChildBlocks`, `getBlockBreadcrumb`, `getBlockIndex`, `getBlocksIndexes`, `getRefIDs`, `getRefText`, `getDOMText`, `getTreeStat`, `getBlocksWordCount`, `getContentWordCount`, `getRecentUpdatedBlocks`, `getDocInfo(s)`, `checkBlockExist`, `checkBlockFold`, `getUnfoldedParentID`, `getBlockSiblingID`, `getBlockRelevantIDs`, `getBlockTreeInfos`, `checkBlockRef`, heading transactions (`getHeadingLevelTransaction`, `getHeadingDeleteTransaction`, `getHeadingInsertTransaction`, `getHeadingChildrenIDs/DOM`).

Mutations (🛡🔒): `insertBlock`, `batchInsertBlock`, `prependBlock`, `appendBlock` (+ batch + daily-note variants), `updateBlock`, `batchUpdateBlock`, `deleteBlock`, `moveBlock`, `moveOutlineHeading`, `foldBlock`, `unfoldBlock`, `setBlockReminder`, `swapBlockRef`, `transferBlockRef`, `appendHeadingChildren`, `updateTaskListItemMarker` (+ batch).

### 11.6 Attribute views / databases (`/api/av/*`)

Render: `renderAttributeView`, `renderHistoryAttributeView`, `renderSnapshotAttributeView`. Keys/fields: `getAttributeViewKeys`, `addAttributeViewKey`, `removeAttributeViewKey`, `sortAttributeViewKey`, `sortAttributeViewViewKey`. Rows/blocks: `addAttributeViewBlocks`, `removeAttributeViewBlocks`, `batchReplaceAttributeViewBlocks`, `duplicateAttributeViewBlock`, `appendAttributeViewDetachedBlocksWithValues`. Values/filters: `setAttributeViewBlockAttr`, `batchSetAttributeViewBlockAttrs`, `getAttributeViewFilterSort`, `setAttrViewGroup`, `changeAttrViewLayout`, `setDatabaseBlockView`. Search/relations: `searchAttributeView`, `searchAttributeViewRelationKey`, `searchAttributeViewRollupDestKeys`, `getMirrorDatabaseBlocks`, `getAttributeViewKeysByAvID/ByID`. Cleanup: `getUnusedAttributeViews`, `removeUnusedAttributeView(s)`.

### 11.7 Search (`/api/search/*`)

`fullTextSearchBlock`, `fullTextSearchAssetContent`, `searchTag`, `searchTemplate`, `searchWidget`, `searchRefBlock`, `searchEmbedBlock`, `getEmbedBlock`, `updateEmbedBlock`, `searchAsset`, `getAssetContent`, `findReplace` 🛡🔒, `removeTemplate` 🛡🔒, `listInvalidBlockRefs`.

### 11.8 Editing pipeline & queries

| Domain | Endpoints |
|---|---|
| `/api/transactions` | `performTransactions` 🛡🔒 — the core batched edit operation the editor uses |
| `/api/query/sql` | `SQL` 🛡🔒 — run a SQL query against the block index |
| `/api/sqlite/flushTransaction` | flush pending DB writes |
| `/api/lute/*` | `spinBlockDOM`, `html2BlockDOM`, `copyStdMarkdown` — Markdown engine ops |
| `/api/template/*` | `render`, `renderSprig`, `docSaveAsTemplate` |
| `/api/convert/pandoc` | Pandoc conversion |
| `/api/format/*` | `autoSpace`, `netImg2LocalAssets`, `netAssets2LocalAssets` |

### 11.9 Assets & files

| Domain | Endpoints |
|---|---|
| `/api/asset/*` | `upload`, `insertLocalAssets`, `resolveAssetPath`, `uploadCloud(ByAssetsPaths)`, `getUnusedAssets`, `getMissingAssets`, `removeUnusedAsset(s)`, `getDocImageAssets`, `getDocAssets`, `renameAsset`, `statAsset`, `setFileAnnotation`, `getFileAnnotation`, `ocr`, `getImageOCRText`, `setImageOCRText`, `fullReindexAssetContent` |
| `/api/file/*` | `getFile`, `putFile`, `copyFile`, `globalCopyFiles`, `workspaceCopyFiles`, `removeFile`, `renameFile`, `readDir`, `getUniqueFilename` |
| `/api/archive/*` | `zip`, `unzip` 🛡🔒 |
| `/api/clipboard/*` | `readFilePaths`, `writeFilePath` |
| `/api/extension/copy` | Web-clipper copy |

### 11.10 Export & import

Export (`/api/export/*`, 🛡): `exportMd`, `exportMds`, `exportNotebookMd`, `exportSY(s)`, `exportNotebookSY`, `exportMdContent`, `exportHTML`, `exportPreviewHTML`, `exportMdHTML`, `exportBrowserHTML`, `exportDocx`, `processPDF`, `exportResources`, `exportAsFile`, `exportData`, `exportDataInFolder`, `exportTempContent`, `export2Liandi`, `exportReStructuredText`, `exportAsciiDoc`, `exportTextile`, `exportOPML`, `exportOrgMode`, `exportMediaWiki`, `exportODT`, `exportRTF`, `exportEPUB`, `exportAttributeView`, `exportCodeBlock`, `preview`.

Import (`/api/import/*`, 🛡🔒): `importStdMd`, `importZipMd`, `importData`, `importSY`.

### 11.11 Sync, repo, and cloud

| Domain | Endpoints |
|---|---|
| `/api/sync/*` | `setSyncEnable`, `setSyncInterval`, `setSyncMode`, `setSyncPerception`, `setSyncGenerateConflictDoc`, `setSyncProvider`, `setSyncProviderS3/WebDAV/Local`, `performSync`, `performBootSync`, `getBootSync`, `getSyncInfo`, cloud-sync-dir CRUD, `export/importSyncProviderS3/WebDAV` |
| `/api/repo/*` | `initRepoKey`, `initRepoKeyFromPassphrase`, `importRepoKey`, `resetRepo`, `purgeRepo`, `purgeCloudRepo`, `createSnapshot`, `tagSnapshot`, `checkoutRepo`, `getRepoSnapshots`, `getRepoTagSnapshots`, `removeRepoTagSnapshot`, cloud snapshot ops, `diffRepoSnapshots`, `openRepoSnapshotFile`, `rollbackRepoSnapshotFile`, `getRepoFile`, `setRepoIndexRetentionDays`, `setRetentionIndexesDaily` |
| `/api/cloud/getCloudSpace` | Cloud quota |
| `/api/account/*` | `login`, `checkActivationcode`, `useActivationcode`, `deactivate`, `startFreeTrial` |
| `/api/setting/getCloudUser`, `logoutCloudUser`, `login2faCloudUser` | Cloud account |

### 11.12 Knowledge features

| Domain | Endpoints |
|---|---|
| `/api/ref/*` | `getBacklink`, `getBacklink2`, `getBacklinkDoc`, `getBackmentionDoc`, `refreshBacklink` |
| `/api/attr/*` | `getBlockAttrs`, `batchGetBlockAttrs`, `setBlockAttrs`, `batchSetBlockAttrs`, `getBookmarkLabels` (`resetBlockAttrs` deprecated) |
| `/api/tag/*` | `getTag`, `renameTag`, `removeTag` |
| `/api/bookmark/*` | `getBookmark`, `renameBookmark`, `removeBookmark` |
| `/api/outline/getDocOutline` | Document outline |
| `/api/graph/*` | `getGraph`, `getLocalGraph`, `resetGraph`, `resetLocalGraph` |
| `/api/riff/*` | Flashcard decks/cards: `createRiffDeck`, `renameRiffDeck`, `removeRiffDeck`, `getRiffDecks`, `addRiffCards`, `removeRiffCards`, `getRiffDueCards` (+tree/notebook), `reviewRiffCard`, `skipReviewRiffCard`, `getRiffCards`, `resetRiffCards`, `batchSetRiffCardsDueTime`, `getRiffCardsByBlockIDs` |
| `/api/history/*` | `getNotebookHistory`, `getDocHistoryContent`, `getHistoryItems`, `searchHistory`, `rollbackDocHistory`, `rollbackNotebookHistory`, `rollbackAssetsHistory`, `rollbackAttributeViewHistory`, `clearWorkspaceHistory`, `reindexHistory` |
| `/api/inbox/*` | `getShorthands`, `getShorthand`, `removeShorthands` |
| `/api/snippet/*` | `getSnippet`, `setSnippet`, `removeSnippet` |

### 11.13 AI, bazaar, storage, UI, network

| Domain | Endpoints |
|---|---|
| `/api/ai/*` | `chatGPT`, `chatGPTWithAction` 🛡 |
| `/api/bazaar/*` | Per package type (plugin/widget/icon/template/theme): `getBazaarX`, `getInstalledX`, `installBazaarX` 🛡🔒, `uninstallBazaarX` 🛡🔒; plus `getBazaarPackageREADME`, `getUpdatedPackage`, `batchUpdatePackage` |
| `/api/petal/*` | `loadPetals`, `setPetalEnabled` — plugin runtime |
| `/api/storage/*` | `getLocalStorage`, `setLocalStorageVal`, `removeLocalStorageVals`, criteria CRUD, recent-docs tracking, outline storage |
| `/api/setting/*` | `setEditor`, `setExport`, `setFiletree`, `setSearch`, `setKeymap`, `setAppearance`, `setIcon`, `setTheme`, `setEmoji`, `setFlashcard`, `setAI`, `setBazaar`, `setPublish`/`getPublish`, `setSnippet`, `setEditorReadOnly`, `setAccount`, virtual-block-ref include/exclude |
| `/api/ui/*` | `reloadUI`, `reloadIcon`, `reloadTheme`, `reloadAttributeView`, `reloadProtyle`, `reloadFiletree`, `reloadTag` |
| `/api/network/*` | `forwardProxy` (SSRF-guarded), `echo` |
| `/api/notification/*` | `pushMsg`, `pushErrMsg` |
| `/api/broadcast/*`, `/ws/broadcast`, `/es/broadcast/subscribe` | Pub/sub broadcast channels (publish, postMessage, getChannels, getChannelInfo) |

> [!NOTE]
> **Deprecated endpoints** are marked in `router.go` and log a warning when used (the `deprecated` handler announces a removal date of `2026-06-30`). Examples being phased out: `/api/system/reloadUI` (→ `/api/ui/reloadUI`), `/api/storage/setLocalStorage` (→ `setLocalStorageVal`), `/api/attr/resetBlockAttrs` (→ `setBlockAttrs`), `/api/filetree/refreshFiletree` (→ `/api/system/rebuildDataIndex`). Migrate off them.

---

## 12. WebSocket Command Channel

The editor uses `/ws` (melody) for realtime, bidirectional communication, distinct from the REST API.

- **Max message size**: 8 MiB.
- **Handshake auth**: On connect, the kernel verifies either a valid session (matching `AccessAuthCode`) or a valid `X-Auth-Token` role (Administrator/Editor/Reader). The auth page keeps a special connection alive (`?app=siyuan&id=auth&type=auth`).
- **Message shape**: clients send `{cmd, reqId, param}`. The kernel builds a `cmd.Command` via `cmd.NewCommand(cmdStr, reqId, param, session)` and runs `cmd.Exec`. Unknown commands return `can not find command [...]`.
- **Read-only enforcement**: Non-read commands are rejected when the kernel is read-only or the token role is Reader/Visitor.
- **Push**: The kernel registers each session in a push channel set and broadcasts progress/notification/reload events to connected UIs.

> [!TIP]
> For integrations, prefer the REST API for discrete operations; the WebSocket is primarily the editor's live transaction/push channel.

---

## 13. WebDAV, CalDAV, and CardDAV

The kernel embeds standards-compliant DAV servers (all under `CheckAuth + CheckAdminRole`, with write methods blocked in read-only mode):

| Service | Path | Backend | Use |
|---|---|---|---|
| **WebDAV** | `/webdav/*` | `golang.org/x/net/webdav` over the workspace dir | Mount your workspace as a network drive |
| **CalDAV** | `/caldav/*`, `/.well-known/caldav` | `emersion/go-webdav/caldav` + SiYuan backend | Expose reminders/dates as calendars |
| **CardDAV** | `/carddav/*`, `/.well-known/carddav` | `emersion/go-webdav/carddav` + SiYuan backend | Expose contacts |

Each DAV family advertises its extended method set (`PROPFIND`, `PROPPATCH`, `MKCOL`, `COPY`, `MOVE`, `REPORT`, etc.) via CORS and gin `Match`.

> [!NOTE]
> A known limitation: the CardDAV backend can't yet satisfy Thunderbird's `PROPFIND` with `current-user-privilege-set`.

---

## 14. Data Model and Storage

### 14.1 On-disk

- **Documents** are `.sy` JSON files (a serialized block tree, `kernel/filesys`).
- **Workspace** layout: `data/` (notebooks + `assets/ emojis/ snippets/ storage/ templates/ widgets/ plugins/ public/`), `conf/`, `history/`, `repo/`, `temp/` (logs at `temp/siyuan.log`).
- Notebooks carry `.siyuan/conf.json` (`BoxConf`).

### 14.2 Indexes (SQLite)

Three SQLite databases are initialized at boot (`kernel/sql`): the **main block index**, the **history index**, and the **asset-content index** (for searching inside attachments). The block index is the backing store for full-text search and `/api/query/sql`. Blocks carry type, content, attributes (IAL), and relationships. The block tree is also indexed in `kernel/treenode`.

> [!IMPORTANT]
> The `.sy` files are the **source of truth**; the SQLite indexes are derived and can be rebuilt (`/api/system/rebuildDataIndex`, `vacuumDataIndex`). If search behaves oddly after manual file changes, rebuild the index.

### 14.3 Block IDs

Every block has a stable unique ID. References, embeds, attributes, flashcards, and the index point at IDs, which is why content can move freely without breaking links.

---

## 15. Synchronization and the Data Repo

Sync is built on **dejavu**, a content-addressed, **encrypted, versioned data repo** (analogous to a Git-like object store), stored in `workspace/repo/`.

### 15.1 Repo key

Before sync/snapshots, initialize an **AES repo key** (`/api/repo/initRepoKey` or `initRepoKeyFromPassphrase`). Data is encrypted with this key before upload. The same key must be set on every device (`importRepoKey`). Losing it (with inconsistent device keys) requires a repo reset.

### 15.2 Providers

`Conf.Sync.Provider`: `0` SiYuan Cloud (membership), `2` S3, `3` WebDAV, `4` local file system. Each has its own config block (endpoint, credentials, timeout, concurrency, TLS-verify, path-style for S3). Sync modes: `1` auto (interval, default 30s), `2` manual, `3` fully manual.

### 15.3 Snapshots

Independent of cloud sync: create/tag/diff/checkout snapshots and roll back individual files or whole snapshots. Auto-purge keeps `indexRetentionDays` (180) of indexes and `retentionIndexesDaily` (2) per day.

> [!CAUTION]
> Never place the workspace inside a third-party file-sync disk (Dropbox/OneDrive/etc.) — concurrent file syncing races with the kernel and can corrupt data. Use SiYuan's own sync to S3/WebDAV/Cloud instead.

---

## 16. Deployment

### 16.1 Docker

```bash
docker run -d \
  -v /siyuan/workspace:/siyuan/workspace \
  -p 6806:6806 \
  -e PUID=1000 -e PGID=1000 \
  b3log/siyuan \
  --workspace=/siyuan/workspace/ \
  --accessAuthCode=please-change-me
```

The image entrypoint (`kernel/entrypoint.sh`) creates a user/group from `PUID`/`PGID`, fixes ownership of `/opt/siyuan`, `/home/siyuan`, and the workspace, then runs the kernel as that user. The program lives under `/opt/siyuan/` (`appearance/`, `guide/`, `stage/`, `kernel`).

**Docker Compose**

```yaml
services:
  main:
    image: b3log/siyuan
    command: ['--workspace=/siyuan/workspace/', '--accessAuthCode=${AuthCode}']
    ports:
      - 6806:6806
    volumes:
      - /siyuan/workspace:/siyuan/workspace
    restart: unless-stopped
    environment:
      - TZ=${YOUR_TIME_ZONE}
      - PUID=${YOUR_USER_PUID}
      - PGID=${YOUR_USER_PGID}
```

**Reverse proxy**: To hide port 6806 behind NGINX, you **must** proxy the WebSocket at `/ws`, and you should **not** use URL rewriting/redirects (they break authentication) — configure a straight reverse proxy.

> [!WARNING]
> Docker/browser limitations (by design): no desktop/mobile app connections (browser only), no PDF/HTML/Word export, no Markdown import.

### 16.2 Unraid / TrueNAS

Both are supported via the same image; set ownership of the appdata/dataset folder to your `PUID:PGID` first (e.g. `chown -R 1000:1000 ...`), then map port `6806` and pass `--accessAuthCode`. See the project README for ready-made templates.

### 16.3 Desktop packaging

`electron-builder` produces per-platform/arch artifacts via the `dist-*` scripts and `electron-builder*.yml` configs (base, arm64, darwin, darwin-arm64, linux, linux-arm64). The Electron app uses `@electron/remote` and disables web security in the renderer because it loads the UI from the local kernel over `http://127.0.0.1:<port>`.

---

## 17. The Publish Service

When `Conf.Publish.Enable` is on, the kernel runs a separate **publish service** (default port `6808`, `kernel/server/proxy.InitPublishService`) that exposes selected documents publicly. It supports **HTTP Basic auth** (`Publish.Auth.Enable`, default on) with a list of `{username, password, memo}` accounts. Combine with per-document **publish access** controls so only intended content is reachable.

> [!CAUTION]
> The publish service makes content reachable without the main access auth code. Keep Basic auth enabled, use strong credentials, and front it with TLS if exposed beyond your LAN.

---

## 18. Operations

### 18.1 Health and status

- **Boot progress**: `GET /api/system/bootProgress`.
- **Version**: `GET /api/system/version`.
- **Logs**: `workspace/temp/siyuan.log` (export via `/api/system/exportLog`).
- **Port discovery**: `~/.config/siyuan/port.json` (PID → port).

### 18.2 Scheduled jobs

`kernel/job` starts a cron (`job.StartCron`) at boot; `model.AutoGenerateFileHistory` and watchers run in the background. File history is generated on `Editor.GenerateHistoryInterval` (default 10 min) and retained for `historyRetentionDays` (30).

### 18.3 Index maintenance

| Operation | Endpoint |
|---|---|
| Rebuild block index | `POST /api/system/rebuildDataIndex` |
| Vacuum index | `POST /api/system/vacuumDataIndex` |
| Reindex asset content | `POST /api/asset/fullReindexAssetContent` |
| Reindex history | `POST /api/history/reindexHistory` |
| Clear temp files | `POST /api/system/clearTempFiles` |

### 18.4 Backup & restore

- **Export/Import Data**: `/api/export/exportData` (+ `exportDataInFolder`) and `/api/import/importData`.
- **Snapshots**: `/api/repo/createSnapshot` + `tagSnapshot`; restore via `checkoutRepo` or `rollbackRepoSnapshotFile`.
- **Filesystem**: copy `workspace/data/` while the kernel is stopped.

### 18.5 Shutdown

`POST /api/system/exit` (desktop) or stop the container/process. The kernel handles OS signals (`model.HandleSignal`) to flush and release the workspace lock cleanly.

---

## 19. Performance Tuning

| Lever | Where | Effect |
|---|---|---|
| `dynamicLoadBlocks` (48–1024, default 192) | `Editor` | Blocks loaded per scroll chunk in big docs |
| `Search.limit` (default 64) | `Search` | Cap on search results |
| Sync `concurrentReqs` / `timeout` | `Sync.{s3,webdav,local}` | Throughput vs. server limits |
| `syncIndexTiming` (12s) | `Repo` | When to warn about slow indexing → suggests purge |
| `indexRetentionDays` / `retentionIndexesDaily` | `Repo` | Local repo size vs. history depth |
| `SIYUAN_PDF_ASSET_CONTENT_INDEX_MAX_SIZE` | env | Skip indexing huge PDFs |
| `Graph.maxBlocks` (10240) | `Graph` | Graph render size |
| `ControlConcurrency` | server | Kernel API requests are serialized for consistency |

- The kernel uses worker pools (`panjf2000/ants`) and in-process caches (`ristretto`, `patrickmn/go-cache`) for hot paths.
- For very large workspaces, periodically **vacuum** the index and purge old repo indexes.

> [!TIP]
> If indexing is slow (>12s), SiYuan prompts to **purge the data repo**, which compacts local indexes and usually restores responsiveness.

---

## 20. Security Hardening Checklist

- [ ] Set a strong **access auth code** (`--accessAuthCode` / `SIYUAN_ACCESS_AUTH_CODE`); never leave it blank on a networked instance.
- [ ] Never set `SIYUAN_ACCESS_AUTH_CODE_BYPASS=true` on anything reachable by others.
- [ ] Keep the kernel bound to `127.0.0.1` unless you intentionally enable `networkServe`.
- [ ] Front any LAN/internet exposure with a **reverse proxy terminating TLS**; proxy `/ws`; avoid URL rewrites.
- [ ] Treat the **API token** (`Conf.API.Token`) as a secret; rotate via `/api/system/setAPIToken`.
- [ ] Enable **HTTPS** (`networkServeTLS`) for direct network serving; distribute the CA via `exportTLSCACert`.
- [ ] Leave **SVG/HTML block scripts disabled** (`AllowSVGScript`/`AllowHTMLBLockScript = false`) unless you fully trust all content.
- [ ] On the **publish service**, keep Basic auth on with strong credentials; scope per-document publish access.
- [ ] Only install **plugins/snippets** from trusted sources (they run with app privileges).
- [ ] Back up the **repo key**; use a passphrase-derived key for portability.
- [ ] For Docker, align `PUID`/`PGID` with volume ownership; don't expose port 6806 directly to the internet.

---

## 21. Extensibility: Plugins, Widgets, and the Bazaar

- **Plugins** use the **petal** API; they're loaded via `/api/petal/loadPetals` and toggled with `setPetalEnabled`. Globally disable the plugin system with `Bazaar.PetalDisabled` (or per-plugin). Installed plugins live in `data/plugins/` and are served (auth-gated) under `/plugins/`.
- **Widgets** are embeddable mini-apps under `data/widgets/`, served under `/widgets/`.
- **Themes/Icons/Templates** install into the appearance/`data/templates` folders; the marketplace (`kernel/bazaar`) fetches packages and READMEs (rendered with Lute), with batch-update support.
- **Snippets** (JS/CSS) provide lightweight customization without packaging a plugin; managed via `/api/snippet/*` and served under `/snippets/` (the snippet `conf.json` requires admin role).

> [!IMPORTANT]
> Plugins and JS snippets execute with the app's privileges. For managed/shared deployments, treat the installed-plugin set and snippet list as a code allowlist and review them.

---

## 22. Code Quality and Conventions

- **Polyglot monorepo**: Go kernel (`kernel/`) + TypeScript UI (`app/`), each with its own build toolchain.
- **Router as source of truth**: `kernel/api/router.go` is the single authoritative endpoint registry; handlers live one-per-domain in `kernel/api/*.go` and delegate to `kernel/model`.
- **Config-as-structs**: each `kernel/conf/*.go` struct owns one concern, with JSON tags and `NewX()` defaults — a clean, discoverable config surface.
- **Vendored renderers**: heavy client-side renderers (KaTeX, Mermaid, ECharts, PDF.js, Graphviz, PlantUML, abcjs, highlight.js) are vendored under `app/stage/protyle/js/` rather than npm-bundled.
- **First-party ecosystem**: core capabilities are split into sibling repos the kernel imports — `lute` (Markdown), `dejavu` (repo/sync), `riff` (FSRS), `httpclient`, `filelock`, `encryption`, `logging` — keeping the main repo focused.
- **Forked dependencies**: `go.mod` `replace`s pin a custom SQLite driver and pdfcpu fork.
- **Internationalization-first**: source comments are predominantly Chinese; UI strings live in `app/appearance/langs/` with English fallback fill-in at serve time.
- **Deprecation discipline**: deprecated endpoints are explicitly flagged with a removal date and a migration target in `router.go`.

---

## 23. Potential Improvements

Observations for maintainers and contributors, grounded in the source and community issues:

- **API method semantics**: nearly all endpoints are registered as `POST` regardless of read/write nature; adopting `GET` for read-only endpoints would improve cacheability and REST conventions (some read endpoints already accept `GET`).
- **CORS breadth**: `Access-Control-Allow-Origin: *` with credentials relies entirely on token/auth-code secrecy. A configurable origin allowlist would reduce CSRF surface for networked deployments.
- **Deprecated-endpoint cleanup**: several endpoints are slated for removal (2026-06-30); ensure plugins/integrations migrate (`reloadUI`, `setLocalStorage`, `resetBlockAttrs`, `refreshFiletree`).
- **Community-requested features** (open issues): a first-class **CLI** (#17674), **MCP (Model Context Protocol)** support (#13795), an **AI agent** (#17797), **semantic search via embeddings** (#17788), and a refreshed **settings UI** (#17601).
- **Docker feature parity**: documenting clearly (and, where feasible, closing) the export/import gaps in the headless deployment would reduce confusion.
- **CardDAV completeness**: handle Thunderbird's `current-user-privilege-set` `PROPFIND`.

---

## 24. Troubleshooting

### Error: `boot kernel failed` / exits with unavailable-port code

- **Cause**: The chosen port is in use, or the kernel can't bind.
- **Solution**: Check `port.json` and free the port, or pass a different `--port`. Inspect `temp/siyuan.log`.

### Symptom: "The workspace is locked"

- **Cause**: Another kernel holds the workspace lock.
- **Solution**: Kill the `SiYuan-Kernel` process (or restart the OS); ensure only one kernel uses a workspace.

### Symptom: Database/index errors at boot ("database is unavailable")

- **Cause**: Corrupt or unreadable SQLite index, or insufficient permissions.
- **Solution**: Verify workspace permissions and disk space; rebuild via `/api/system/rebuildDataIndex` once booted. Your `.sy` files remain intact.

### Error: S3 sync `put chunk failed:`

- **Cause**: Wrong endpoint/region/keys/bucket or TLS/path-style mismatch.
- **Solution**: Re-verify `Sync.s3` settings; try `pathStyle`/`skipTlsVerify`; confirm write permissions on the bucket.

### Error: `ip address [%s] is prohibited`

- **Cause**: The forward-proxy/echo endpoint blocked a request to a disallowed IP (SSRF guard).
- **Solution**: Expected for private/blocked targets; route legitimate external requests through an allowed host.

### Error: `not found executable pandoc`

- **Cause**: A Pandoc-dependent export with no Pandoc available (headless/Docker or bad `pandocBin`).
- **Solution**: Use the desktop app or set a valid `Export.PandocBin`.

### Error: `require upgrade SiYuan to use this plugin [...]`

- **Cause**: Plugin needs a newer kernel.
- **Solution**: Update SiYuan.

### Error: TLS errors (`failed to decode CA certificate PEM`, `the provided certificate is not a CA certificate`)

- **Cause**: Importing an invalid TLS CA bundle.
- **Solution**: Provide a valid PEM CA cert+key; regenerate via `exportTLSCACert` if unsure.

### Symptom: Reverse proxy works for pages but realtime updates are broken

- **Cause**: `/ws` WebSocket isn't proxied (or URL rewriting interferes with auth).
- **Solution**: Add an explicit WebSocket proxy for `/ws`; remove rewrite rules.

---

## 25. Quick Reference

### 25.1 Ports & paths

| Item | Value |
|---|---|
| Kernel HTTP/WS | `6806` (default; `--port 0` picks free) |
| Publish service | `6808` |
| Bind | `127.0.0.1` (or `0.0.0.0` with `networkServe`/Docker) |
| Port discovery file | `~/.config/siyuan/port.json` |
| Default workspace | `~/SiYuan/` |
| Log | `workspace/temp/siyuan.log` |
| Config | `workspace/conf/conf.json` |
| Data repo | `workspace/repo/` |

### 25.2 Kernel flags

`--workspace --wd --port --readonly --accessAuthCode --ssl --lang --mode`

### 25.3 Auth model

| Layer | Mechanism |
|---|---|
| Access auth code | Session password (`/check-auth`) |
| API token | `X-Auth-Token` (JWT; `Conf.API.Token`) |
| Roles | Administrator / Editor / Reader / Visitor |
| Middleware | `CheckAuth` → `CheckAdminRole` → `CheckReadonly` |

### 25.4 Key API entry points

| Need | Endpoint |
|---|---|
| List notebooks | `POST /api/notebook/lsNotebooks` |
| Create doc from MD | `POST /api/filetree/createDocWithMd` |
| Insert/update/delete block | `POST /api/block/{insert,update,delete}Block` |
| Batched edit | `POST /api/transactions` |
| SQL query | `POST /api/query/sql` |
| Full-text search | `POST /api/search/fullTextSearchBlock` |
| Export Markdown | `POST /api/export/exportMd` |
| Trigger sync | `POST /api/sync/performSync` |
| Get config | `POST /api/system/getConf` |

### 25.5 Core dependencies

| Library | Role |
|---|---|
| `gin-gonic/gin`, `olahol/melody` | HTTP / WebSocket |
| `88250/lute` | Markdown engine |
| `mattn/go-sqlite3` (88250 fork) | Block index |
| `siyuan-note/dejavu` | Encrypted data repo / sync |
| `siyuan-note/riff` + `open-spaced-repetition/go-fsrs` | Flashcards |
| `aws-sdk-go-v2/s3`, `studio-b12/gowebdav`, `emersion/go-webdav` | Sync, CalDAV/CardDAV |
| `klippa-app/go-pdfium`, `pdfcpu` (88250 fork) | PDF |
| `otiai10/gosseract` | OCR (Tesseract) |
| `sashabaranov/go-openai` | AI |
| `golang-jwt/jwt`, `gin-contrib/sessions`, `steambap/captcha` | Auth |
| `xuri/excelize` | Spreadsheet export |

---

_Generated June 2026 from siyuan-note/siyuan at 96dfe0b. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
