Dingovault
中文文档 | 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@latest
Go 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.
Why Dingovault?
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.”
v1.4.2 — AGPL-3.0, GHCR image & npm SDK stub
Details: CHANGELOG.md.
- License: AGPL-3.0; GHCR ships
ghcr.io/cndingbo2030/dingovault on each tag; @cndingbo2030/dingovault-sdk on GitHub Packages.
v1.4.1 — Workflow fixes & semantic release filenames
Details: CHANGELOG.md.
- Android CI: stable NDK / SDK env (
ANDROID_HOME, NDK_HOME, ndk-bundle symlink) 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.
v1.4.0 — Android gomobile library & responsive UI
Details: CHANGELOG.md.
- gomobile
.aar under cmd/dingovault-android/mobile plus a minimal Gradle shell for CI-built APK / AAB artifacts on version tags.
- Scoped storage helper (
AndroidScopedVaultPath) for vault roots under getExternalFilesDir/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.
v1.3.2 — WebDAV, S3-compatible sync & LAN discovery
Details: CHANGELOG.md.
- WebDAV two-way sync for
.md files with timestamp + size rules; divergent edits become *.conflict.md siblings.
- 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).
v1.3.0 — AI writing & smart links
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.
v1.2.0 Master Release
- 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.
Why teams pick Dingovault (technical checklist)
- Benchmarks, not vibes: run
make benchmark for 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 & contact
Stack
| 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 |
Architecture
storage.Provider
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.
SaaS server
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.
Schema migrations & plugins (v1.0+)
- First launch: If you start the desktop app with no
-notes flag and no saved vaultPath, Dingovault unpacks the embedded demo-vault/ into your OS cache and opens it so you can try the outliner immediately. Set DINGO_NO_DEMO_VAULT=1 to disable that behavior.
- SQLite
user_version: On open, internal/storage/migrate.go runs incremental migrations so upgrades can add tables/columns without wiping existing notes. Bump CurrentSchemaVersion and add a new step when the schema changes.
before:block:save: Register on the graph service’s bus with RegisterBeforeBlockSave to mutate markdown before it is written (e.g. auto-formatting, AI hooks). Errors abort the save.
after:block:indexed: Pub/sub topic after:block:indexed fires after a source is reindexed (alongside the existing file reindex topic).
- Reference AI plugin:
internal/plugins/summarizer subscribes to after:block:indexed; when a block includes #summarize, it appends a generated child summary and reindexes through storage.Provider.
- Desktop UI: External scripts can call
window.__DINGOVAULT__.registerToolbarButton / registerSidebarSection (see frontend/src/pluginRegistry.js). Missing images get a placeholder via initImageFallback().
Production hardening (SaaS)
| 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. |
Health & metrics
| 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. |
Mobile / automation
- Siri Shortcuts / widgets:
POST /api/v1/capture with a Bearer token is enough to append to an inbox page without loading the block tree.
- CORS: set
ALLOWED_ORIGINS so browser-based or extension clients can call the API from your SPA origin.
Desktop UX (Phase 14)
- Fold: parent blocks can be collapsed; state is stored in
localStorage per 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.
Mobile & privacy (Phase 15)
- Responsive UI: touch-sized controls,
16px textarea 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=s3 to store uploads in S3-compatible storage; markdown uses DINGO_S3_PUBLIC_BASE URLs.
- Releases: push a tag
v1.2.3 to 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.
Usage
Prerequisites
- Go (matching
go.mod)
- Node.js + npm (for the frontend)
- Wails CLI v2 for desktop builds
Desktop (local SQLite)
make dev
# or
wails dev
Point -notes at your vault directory (saved in user config after first run).
Desktop (cloud / SaaS index)
-
Run a SaaS server (see below) and obtain a JWT, e.g.
POST /api/v1/auth/token with {"userId":"your-id"} (dev-style; replace with real auth in production).
-
Enable cloud mode via config (~/.config/dingovault/config.json on 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=1
DINGO_CLOUD_URL=http://127.0.0.1:12030
DINGO_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.
SaaS API server (CLI)
Development (default dev JWT secret allowed):
DINGO_SERVER=1 go run ./cmd/dingovault -db=./dingovault_saas.db
Production (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.db
Or only set DINGO_PORT (also enables HTTP):
DINGO_PORT=12030 go run ./cmd/dingovault -server -db=./dingovault_saas.db
Docker (SaaS image)
The 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:latest
Health: 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.
macOS installation (Gatekeeper)
If macOS shows a malware/untrusted developer warning for the downloaded app:
- Right-click
Dingovault.app and choose Open (then confirm).
- If needed, clear quarantine metadata manually:
xattr -cr /Applications/Dingovault.app
This is expected for unsigned open-source app bundles.
Makefile targets
| 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). |
API overview (v1)
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.
Container image (GitHub Container Registry)
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:latest
Image labels include org.opencontainers.image.source and AGPL-3.0. See Dockerfile.
Plugin SDK stub (GitHub Packages / npm)
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.
License
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.