中文文档 | English
High-performance, local-first outliner with SaaS sync, built in Go.
From source (CLI / server binary):
go install github.com/cndingbo2030/dingovault/cmd/dingovault@latestGo module path: github.com/cndingbo2030/dingovault
Dingovault is a block-based Markdown vault: fast full-text search (FTS5), wikilinks, YAML frontmatter, and a clean desktop shell. The same core runs offline against embedded SQLite or online against your self-hosted or managed SaaS API—switchable via a small storage.Provider abstraction.
Speed that feels like zero latency. Dingovault is built for people who live in their notes. Full-text search over your vault is tuned until it is effectively instant—warm local runs are commonly around ~1ms median for FTS queries and sub‑millisecond page loads on typical hardware (always measure yours with make benchmark). No spinners for “finding that one bullet.”
Military‑grade privacy for your words. Your vault can sit behind AES‑256‑GCM encryption at rest (DINGO_MASTER_KEY), so disk theft does not mean plaintext theft. Pair that with JWT‑protected SaaS APIs when you self‑host the server, and you keep multi‑user deployments on a short leash.
AI without shipping your brain to the cloud. Dingovault talks to Ollama and other local endpoints first: inline assist, vault‑aware chat, and optional embeddings stay on your machine when you want them to—so “RAG over my notes” does not have to mean “send every block to someone else’s GPU.”
Details: CHANGELOG.md.
- License: AGPL-3.0; GHCR ships
ghcr.io/cndingbo2030/dingovaulton each tag;@cndingbo2030/dingovault-sdkon GitHub Packages.
Details: CHANGELOG.md.
- Android CI: stable NDK / SDK env (
ANDROID_HOME,NDK_HOME,ndk-bundlesymlink) and gomobile init ordering. - Release assets: descriptive names (macOS-Apple-Silicon-M1-M2-M3, macOS-Intel-Processor, Windows-64bit-Installer, Linux-Desktop-amd64, Android-Universal-Install, etc.).
- UI: safe-area + dynamic header for tablet landscape vs phone.
Details: CHANGELOG.md.
- gomobile
.aarundercmd/dingovault-android/mobileplus a minimal Gradle shell for CI-built APK / AAB artifacts on version tags. - Scoped storage helper (
AndroidScopedVaultPath) for vault roots undergetExternalFilesDir/app-specific external storage. - Responsive UI: dynamic viewport height, 48px touch targets, bottom tab bar on phones, three-column outline / semantic / backlinks layout from 900px width up.
Details: CHANGELOG.md.
- WebDAV two-way sync for
.mdfiles with timestamp + size rules; divergent edits become*.conflict.mdsiblings. - S3-compatible object storage sync with the same conflict rules (AWS S3, MinIO, and other API-compatible hosts).
- mDNS LAN discovery plus 4-digit PIN pairing to copy sync settings between machines on the same Wi‑Fi (bridge API + bindings; wire your own Sync Settings UI or call the Wails methods).
Full user-facing notes: see CHANGELOG.md.
- Real-time inline AI: streaming text as the model writes, with graceful recovery if the AI server disconnects.
- Vault-aware chat: answers combine the open page with semantically related blocks from your notes (optional embeddings; local-first).
- Semantic related & graph: discover similar blocks and optional meaning-based links alongside wikilinks.
- Smarter tag suggestions based on how each block reads.
- New premium visual identity with refreshed app branding and icon pipeline.
- 100% clean-code push: complex paths refactored for better maintainability.
- Enhanced macOS Gatekeeper guidance for unsigned app bundles.
- Benchmarks, not vibes: run
make benchmarkfor FTS p50 / page-load p50 on your disk (see Why Dingovault? above for typical ballparks). - Encryption & SaaS hardening:
DINGO_MASTER_KEY(AES‑256‑GCM at rest) and JWT‑gated HTTP APIs for multi‑tenant or team installs. - Plugin-ready architecture: backend hooks (
before:block:save,after:block:indexed) and frontend plugin slots make it easy to extend without forking core logic.
| Maintainer | cndingbo2030 |
| cndingbo@outlook.com | |
| Repository | github.com/cndingbo2030/dingovault |
| Layer | Technology |
|---|---|
| Runtime | Go (see go.mod for the exact toolchain) |
| Desktop | Wails v2 + webview |
| UI | Svelte + Vite |
| Local index | SQLite + FTS5 (modernc.org/sqlite) |
| Markdown | Goldmark |
| SaaS API | net/http, JWT (HS256), REST under /api/v1 |
All graph and UI persistence go through storage.Provider (internal/storage/provider.go):
Store(internal/storage/sqlite.go) — local SQLite + triggers keeping FTS in sync.RemoteStore(internal/storage/remote.go) — HTTP client:Authorization: Bearer <JWT>on every call, mapping to the same REST routes the SaaS server exposes.
The graph service (internal/graph) and Wails bridge (internal/bridge) depend only on Provider, not on SQL or HTTP—so the desktop app can run in local or cloud mode without forking business logic.
cmd/dingovault can run an HTTP server on DINGO_PORT (default 12030), with routes registered in internal/server/handlers.go. Protected handlers read the tenant from the JWT (sub) and scope SQLite rows by user_id.
- Local FTS: prefix-token queries over FTS5 are typically around ~1ms p50 on modest vaults after warmup.
- Page load: serving one page’s block tree from SQLite is typically around ~0.2ms p50 of database work on warm local cache.
Exact numbers depend on hardware, vault size, and OS cache—run make benchmark on your machine for a reproducible report.
Encrypted stress + integrity: make benchmark-encrypted sets DINGO_MASTER_KEY and runs the benchmark with -verify so decrypted block content is spot-checked after indexing.
- First launch: If you start the desktop app with no
-notesflag and no savedvaultPath, Dingovault unpacks the embeddeddemo-vault/into your OS cache and opens it so you can try the outliner immediately. SetDINGO_NO_DEMO_VAULT=1to disable that behavior. - SQLite
user_version: On open,internal/storage/migrate.goruns incremental migrations so upgrades can add tables/columns without wiping existing notes. BumpCurrentSchemaVersionand add a new step when the schema changes. before:block:save: Register on the graph service’sbuswithRegisterBeforeBlockSaveto mutate markdown before it is written (e.g. auto-formatting, AI hooks). Errors abort the save.after:block:indexed: Pub/sub topicafter:block:indexedfires after a source is reindexed (alongside the existing file reindex topic).- Reference AI plugin:
internal/plugins/summarizersubscribes toafter:block:indexed; when a block includes#summarize, it appends a generated child summary and reindexes throughstorage.Provider. - Desktop UI: External scripts can call
window.__DINGOVAULT__.registerToolbarButton/registerSidebarSection(seefrontend/src/pluginRegistry.js). Missing images get a placeholder viainitImageFallback().
| Variable | Purpose |
|---|---|
DINGO_ENV=production |
Enables strict JWT rules: DINGO_JWT_SECRET is required and must not be the built-in development default. The Docker image sets this by default. |
DINGO_JWT_SECRET |
HS256 signing key (minimum 16 characters). Generate a long random string for production. |
ALLOWED_ORIGINS |
Optional comma-separated exact browser origins allowed for CORS (e.g. https://app.example.com,http://localhost:5173). If unset, no Access-Control-Allow-Origin is sent (non-browser / same-origin clients unaffected). |
DINGO_MASTER_KEY |
Optional passphrase: AES-256-GCM encryption for block content in SQLite (dv1: prefix). FTS body search is ineffective on ciphertext. Losing or changing the key makes encrypted rows unreadable. |
DINGO_BLOB_BACKEND |
filesystem (default) or s3 / minio for POST /api/v1/assets. |
| S3 / MinIO | DINGO_S3_BUCKET, DINGO_S3_REGION (default us-east-1), optional DINGO_S3_ENDPOINT, DINGO_S3_PREFIX, DINGO_S3_PUBLIC_BASE (no trailing slash; markdown links), DINGO_S3_USE_PATH_STYLE=1. Auth: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY or DINGO_S3_ACCESS_KEY / DINGO_S3_SECRET_KEY. |
| Endpoint | Auth | Description |
|---|---|---|
GET /api/v1/health |
Public | Liveness JSON {"status":"ok"}. |
GET /api/v1/sys/stats |
JWT | Global index stats: blockCount, pageCount, tenantCount (distinct user_id in blocks; tenants with no blocks are not counted). |
POST /api/v1/capture |
JWT | Quick capture: JSON {"text":"…","sourcePath":"Inbox.md"} (default path Inbox.md). Appends a bullet under the vault page and reindexes. Requires -notes / vault path on the server. |
POST /api/v1/assets |
JWT | Multipart field file. Filesystem mode: vault/assets/ (needs vault path). S3 mode: DINGO_BLOB_BACKEND=s3 + bucket env (no local vault required for uploads). Returns path, markdown, bytes. |
GET /api/v1/graph/wiki |
JWT | Page-level graph: nodes (id = absolute path, label) and edges (source → target) from resolved wikilinks. Requires vault path for alias resolution. |
- Siri Shortcuts / widgets:
POST /api/v1/capturewith a Bearer token is enough to append to an inbox page without loading the block tree. - CORS: set
ALLOWED_ORIGINSso browser-based or extension clients can call the API from your SPA origin.
- Fold: parent blocks can be collapsed; state is stored in
localStorageper page (dingovault-collapse:<path>), so large outlines stay manageable without changing Markdown yet. - Drag ⋮⋮: reorder sibling blocks (same parent) via the Wails binding
ReorderBlockBefore(file-backed, same rules as indent/outdent). - Graph button: force-directed page link graph (resolved wikilinks) using d3-force.
- Responsive UI: touch-sized controls,
16pxtextarea font (reduces iOS zoom), safe-area padding, stacked toolbar on narrow screens. - Swipe on the left rail: left = cycle TODO (same as desktop shortcut); right = clear block text (confirm). Gestures use the gutter so vertical scrolling in the editor stays natural.
- Cloud assets: enable
DINGO_BLOB_BACKEND=s3to store uploads in S3-compatible storage; markdown usesDINGO_S3_PUBLIC_BASEURLs. - Releases: push a tag
v1.2.3to run.github/workflows/release.yml(Go tests,dingovault-server-*CLI matrix, Wails builds for Linux / macOS / Windows). Adjust runner packages if a platform fails in your fork.
- Go (matching
go.mod) - Node.js + npm (for the frontend)
- Wails CLI v2 for desktop builds
make dev
# or
wails devPoint -notes at your vault directory (saved in user config after first run).
-
Run a SaaS server (see below) and obtain a JWT, e.g.
POST /api/v1/auth/tokenwith{"userId":"your-id"}(dev-style; replace with real auth in production). -
Enable cloud mode via config (
~/.config/dingovault/config.jsonon Linux):{ "vaultPath": "/path/to/your/markdown/vault", "cloudMode": true, "cloudApiUrl": "http://127.0.0.1:12030", "cloudToken": "<paste JWT here>" }Or use environment variables (override config):
DINGO_CLOUD_MODE=1DINGO_CLOUD_URL=http://127.0.0.1:12030DINGO_CLOUD_TOKEN=<jwt>
-
Start the desktop app as usual (
wails dev/wails build). The vault folder remains the source of truth on disk; indexing pushes parsed content to the API on full scan, watcher events, and edits.
Development (default dev JWT secret allowed):
DINGO_SERVER=1 go run ./cmd/dingovault -db=./dingovault_saas.dbProduction (strict secret):
export DINGO_ENV=production
export DINGO_JWT_SECRET='your-unique-secret-at-least-16-chars'
export ALLOWED_ORIGINS='https://your-spa.example.com'
DINGO_PORT=12030 go run ./cmd/dingovault -server -db=./dingovault_saas.dbOr only set DINGO_PORT (also enables HTTP):
DINGO_PORT=12030 go run ./cmd/dingovault -server -db=./dingovault_saas.dbThe image sets DINGO_ENV=production; you must pass a real JWT secret at run time:
make deploy-saas
docker run --rm -p 12030:12030 \
-e DINGO_JWT_SECRET='your-unique-secret-at-least-16-chars' \
-e ALLOWED_ORIGINS='https://app.example.com' \
-v dingovault-data:/data \
dingovault-saas:latestHealth: GET http://localhost:12030/api/v1/health
Stats (JWT): GET http://localhost:12030/api/v1/sys/stats
For /api/v1/capture, /api/v1/assets, and /api/v1/graph/wiki, run the binary with -notes / a mounted vault directory (same as non-Docker SaaS). The stock Docker example is API-oriented; mount your Markdown tree and pass -notes=/vault (or extend the image entrypoint) if you need those routes.
If macOS shows a malware/untrusted developer warning for the downloaded app:
- Right-click
Dingovault.appand choose Open (then confirm). - If needed, clear quarantine metadata manually:
xattr -cr /Applications/Dingovault.appThis is expected for unsigned open-source app bundles.
| Target | Purpose |
|---|---|
make dev |
Wails dev (desktop) |
make build / make release |
Production Wails build |
make benchmark |
Index + FTS stress benchmark |
make benchmark-encrypted |
Same with DINGO_MASTER_KEY + -verify (integrity spot-check) |
make fmt |
go fmt ./... |
make lint-frontend |
Svelte / TS checks |
make dist |
Zip app bundle + help vault |
make deploy-saas |
Build dingovault-saas:latest Docker image |
Git tag v* |
Triggers release workflow: tests, cross-platform server binaries, Wails desktop artifacts (see .github/workflows/release.yml). |
Public: GET /api/v1/health, POST /api/v1/auth/token
Protected (Authorization: Bearer …): blocks, pages, search, backlinks, alias resolve, POST /api/v1/pages/reindex, GET /api/v1/sys/stats, POST /api/v1/capture, POST /api/v1/assets, GET /api/v1/graph/wiki, etc. See internal/server/handlers.go and internal/server/phase14_handlers.go.
Upstream: github.com/cndingbo2030/dingovault
Also see project.meta.json.
On each v* tag, CI builds and pushes the SaaS server image:
docker pull ghcr.io/cndingbo2030/dingovault:v1.4.2
docker run --rm -p 12030:12030 -e DINGO_JWT_SECRET='your-secret-at-least-16-chars' -v dingovault-data:/data ghcr.io/cndingbo2030/dingovault:latestImage labels include org.opencontainers.image.source and AGPL-3.0. See Dockerfile.
The scoped package @cndingbo2030/dingovault-sdk is published from sdk/ on each release tag (stub today; reserves the name for future typed APIs). Install instructions: sdk/README.md.
This project is licensed under the GNU Affero General Public License v3.0 — see LICENSE. If you run a networked modified version, AGPL requires you to offer corresponding source to users interacting with it over a network.