The Problem

SiYuan solves the problem of personal knowledge management (PKM) for users who want full control over their data. Most PKM tools are either closed-source, cloud-only, or both, which means your notes live on someone else's server. SiYuan is a self-hosted, open-source alternative that keeps your data local or on infrastructure you control.

What This Does

SiYuan is a privacy-first, self-hosted personal knowledge management system. It is a fork of the popular siyuan-note/siyuan project (46k+ stars). The repository contains a full application: a rich-text editor with block-based note-taking, a Go-based kernel that handles data persistence and a REST API, and an Electron shell for desktop deployment.

The codebase is split into two main projects: app/ (the TypeScript/React frontend) and kernel/ (the Go backend). The frontend handles the editor UI, while the kernel manages the data layer, including the database and file system operations. A Dockerfile is present for containerized deployment.

How It Is Wired

The application starts in the frontend. The entry point is app/electron/main.js for the desktop app, which loads app/appearance/boot/index.html. From there, app/src/index.ts is the main frontend module, importing 27 modules and being imported by 80.

The frontend communicates with the Go kernel via a REST API. The kernel, built from kernel/go.mod, handles file persistence and data operations. The API surface is documented in API.md.

The module graph shows a clear hub-and-spoke pattern. app/src/constants is the central hub, imported by 198 modules. app/src/util/fetch (imported by 160 modules) handles HTTP calls to the kernel. These are high-blast-radius modules—changes to them ripple across the entire frontend.

There are 240 modules inside circular dependencies. This means some import cycles exist (e.g., app/src/util/fetch.ts participates in one), which can complicate refactoring and testing. The app/src/protyle/util/hasClosest function is a utility with 134 importers and zero dependencies, making it a stable leaf node.

The kernel side has 236 Go files. The exact data flow from frontend to disk is not fully mapped in this analysis, but the REST API over HTTP is the primary boundary between the two projects.

How To Use It

Setup (from the README and Dockerfile):

# Clone the repository
git clone https://github.com/moses-y/siyuan

# For development, you'll need both Node and Go toolchains
cd app && npm install
cd ../kernel && go mod download

# Or build the Docker image
docker build -t siyuan .

Running it: The README documents Docker hosting as the simplest path:

docker run -v /path/to/data:/home/siyuan/data -p 6806:6806 siyuan

The kernel exposes its API on port 6806. For desktop use, the Electron app (app/electron/main.js) is the entry point. The README also documents installation packages for Windows, macOS, and Linux.

Real-World Use

A typical deployment is a small team or individual running SiYuan in Docker on a home server or VPS. With the data volume mounted, notes persist across container restarts. The web interface is accessible via the mapped port, and the REST API allows integration with other tools. For example, a script could push meeting notes into a specific notebook using the API documented in API.md.

Code Health & Issues

Static analysis (not opinion) found 709 issues across 5 categories:

  • High - Import cycle members (19 instances): app/src/util/fetch.ts, app/src/protyle/util/compatibility.ts, app/src/index.ts participate in circular imports. This complicates refactoring and can cause initialization-order bugs.
  • High - Deep nesting (19 instances): app/src/constants.ts, app/src/index.ts, app/src/protyle/util/compatibility.ts have control flow nested up to 7 levels deep.
  • High - Oversized files (9 instances): app/src/constants.ts is 824 lines. High fan-in plus large size means changes there have wide blast radius.
  • High - Hub modules (7 instances): app/src/constants.ts (198 dependents) and app/src/util/fetch.ts (160 dependents) are the riskiest files to modify.
  • Medium - High branching density (6 instances): app/src/util/fetch.ts has 45 branch points over 135 lines.

Additional health audit findings:

  • High - Unpinned GitHub Actions: .github/workflows uses mutable tags (e.g., @v4.1.2, @master) instead of commit SHAs. A moved tag could compromise CI secrets.
  • Med - No least-privilege token: Workflows referencing secrets don't declare permissions, so the token inherits repo defaults.
  • Med - No dependency update bot: No Dependabot or Renovate configured.
  • Med - Mutable Docker base images: node:21, golang:1.25-alpine, alpine:latest in the Dockerfile.
  • Med - No dependency vulnerability scan in CI.
  • Med - Large binaries in repo: app/pandoc/pandoc-windows-amd64.zip is 36.4MB; 7 blobs over 5MB total.
  • Med - persist-credentials: false not set on checkout in .github/workflows/cd.yml.
  • Med - No non-root USER in Dockerfile.
  • Med - Test coverage is minimal: 1 test file against 622 source files (ratio 0.002).
  • Low - No job timeouts in workflows.

The Bottom Line

This is a mature, feature-complete PKM application with a solid architecture: a clean split between a Go kernel and a TypeScript frontend. The main risks are the hub modules with high fan-in, circular imports, and CI/CD hygiene issues (unpinned actions, mutable base images, no dependency scanning). The test coverage is effectively nonexistent, so changes to core modules carry real regression risk. If you need a self-hosted PKM system and accept the maintenance burden, this is a strong candidate—but treat the lack of tests as a known hazard before making deep changes.