Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

204 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

symaira-desktop (symdesk)

CI Release License

Symaira Desktop social preview

The composition shell of the Symaira ecosystem.

A local-first and self-hostable, agent-native workspace that unifies documents, notes, knowledge and AI over a single plain-Markdown vault. It is the realization of the "Paperless + Obsidian + Notion AI" dream as one surface, deliberately not a monolith and without making a proprietary database the source of truth.

symdesk is a Go core that can run as a CLI, MCP server, authenticated self-hosted document server, or distributed OCR worker. Native SwiftUI apps for macOS and iPhone/iPad can either open a local/iCloud vault directly or connect to the self-hosted server as frontends.

  • Owns: the Markdown-as-SSOT vault contract, one SQLite sidecar index, the native app, and the runtime composition layer
  • Composes at runtime today: symaira-ingest (OCR/ingest), symseek (search), symmemory (RAG/graph), symfetch (web clipping), symrelate (opaque contact references in meeting notes), and symvault (secrets resolution), all detected dynamically via PATH probe (the core never depends on their presence and degrades gracefully).
  • Runs on: macOS, Linux amd64/arm64, Docker/Compose, Raspberry Pi, Mac mini, NAS-class hosts, and Home Assistant OS as an app/add-on
  • Delegates (does not rebuild): spreadsheets → LibreOffice; code editing → editor plugins + agent orchestration; Obsidian Canvas/whiteboards → Obsidian; iCloud sync → the OS
  • Language: Go (CGO-free, core) + Swift/SwiftUI (app). License: Apache-2.0

SymDesk architecture

Why SymDesk

  • One plain-Markdown vault remains the source of truth instead of locking data into a proprietary database.
  • The CLI, MCP server, and native macOS app share the same service layer and contracts.
  • Symaira tools are composed at runtime, so optional capabilities degrade gracefully when a companion tool is unavailable.
  • Local-first and self-hosted modes both keep documents, indexes, and configuration under the user’s control.
  • OCR jobs can run beside the server or be leased to a stronger Mac/Linux worker, including a MacBook using a local Ollama vision model.

Status

Working MVP: Go core (symdesk) with CLI, stdio MCP, SQLite/FTS5 sidecar, vault contract v2 (VAULT.md), authenticated HTTP API, durable document queue, Docker deployment, and distributed Tesseract/Ollama OCR workers. The native macOS app provides a live dashboard, editing, sidebar note creation, full-text search, link graph, saved views, document workflow, ingest, companion tools installer, and previews in local and server-connected modes. The iOS companion provides cached on-device search, document filters, Markdown reading, and native Quick Look previews from either Files/iCloud or a connected server.

This is not yet complete Paperless-ngx parity. The self-hosted path already covers central originals, Markdown, index, upload queue, remote OCR, native clients, IMAP mail ingestion, users/groups/document-level permissions, storage-path templating and retention rules, and expiring share links for unauthenticated document access; a browser admin UI remains future work.

For recovery guidance, see BACKUP.md.

Installation

CLI from GitHub Releases

Download the archive for your platform from the latest GitHub Release, extract it, and place symdesk on your PATH.

CLI from source

go install github.com/danieljustus/symaira-desktop/cmd/symdesk@latest

Native macOS app from source

The SwiftUI app currently builds from source on macOS 14 or newer:

brew install xcodegen
xcodegen generate
xcodebuild build -project SymDesk.xcodeproj -scheme SymDesk -destination 'platform=macOS'

Native iPhone/iPad app from source

The mobile companion supports iOS 18 or newer. Generate the project, open it in Xcode, choose your development team for the SymDeskMobile target, and run it on an iPhone or iPad:

xcodegen generate
open SymDesk.xcodeproj

On first launch, choose a vault in Files/iCloud or enter a SymDesk Server URL and token. Server snapshots are searched on-device and originals are downloaded only when a preview is opened.

What can be built and connected?

Artifact Build/run target Storage and processing Connects to
symdesk CLI/MCP macOS, Linux, Windows; amd64/arm64 Local vault and optional Symaira tools Shell, agents, native Mac app
SymDesk Server container Docker/Compose on Mac mini, Raspberry Pi 4/5 (64-bit), Linux server or NAS Owns vault, originals, FTS index and durable OCR queue Mac app, iOS app, workers
Home Assistant app/add-on Home Assistant OS on aarch64/amd64 Persistent /data/vault; optional in-container Tesseract Mac app, iOS app, remote workers
Remote OCR worker macOS or Linux Downloads one leased original, runs Tesseract or local Ollama, returns text One SymDesk Server
SymDesk.app macOS 14+ Direct local vault or native server frontend; edits and uploads Local CLI or SymDesk Server
SymDesk for iPhone/iPad iOS/iPadOS 18+ Direct Files/iCloud reader or cached server frontend Files provider or SymDesk Server

The server and clients use the same versioned /api/v1 contract. Access tokens are stored in Apple Keychain. A plain HTTP server should only be used on a trusted LAN; use HTTPS or a VPN for access outside it.

Self-hosting quick start

Create a random token and start the server:

export SYMDESK_SERVER_TOKEN="$(openssl rand -hex 32)"
docker compose up -d symdesk-server

The persistent vault and server state are stored in ./data. Connect the Mac or iOS app to http://SERVER-IP:8787 with the same token. Do not forward that plain HTTP port directly to the internet.

To process OCR on the server with Tesseract:

docker compose --profile local-processing up -d

To keep a Raspberry Pi or Mac mini focused on storage and run OCR through Ollama on a MacBook instead, give the worker its own credential instead of reusing the client/admin token — a worker token can only lease, download and complete OCR jobs, not read or write arbitrary vault files:

export SYMDESK_WORKER_TOKEN="$(openssl rand -hex 32)"
docker compose up -d symdesk-server  # picks up SYMDESK_WORKER_TOKEN automatically

ollama pull gemma3
symdesk worker \
  --server http://SERVER-IP:8787 \
  --token "$SYMDESK_WORKER_TOKEN" \
  --engine ollama \
  --ollama-model gemma3

Existing single-token deployments keep working unchanged: leave SYMDESK_WORKER_TOKEN unset and workers can keep authenticating with SYMDESK_SERVER_TOKEN during the migration. See Self-hosting SymDesk for the full migration path.

The worker may come and go. Pending jobs remain on the server, expired leases are recovered, and no network share needs to be mounted on the MacBook. For a full Docker command, Home Assistant installation, storage layout, HTTPS advice, backup notes and worker options, see Self-hosting SymDesk.

Development

make build          # → bin/symdesk
make test           # go test -race ./...
make benchmark-large # generate and index a deterministic 10k-document vault
docker compose build # multi-architecture-compatible Linux server/worker image

macOS app (requires xcodegen)

xcodegen generate
xcodebuild build -project SymDesk.xcodeproj -scheme SymDesk -destination 'platform=macOS'

CLI sample

$ symdesk version --json
{"tool":"symdesk","version":"0.7.3","schema_version":1}

Usage

export SYMDESK_VAULT=~/Vault   # or configure via config file / --vault

symdesk index                  # index the vault into the sidecar
symdesk ls                     # list notes
symdesk search "invoice 2026"  # FTS5 full-text search
symdesk note new "My Note"     # create a note (frontmatter per vault contract)
symdesk props <file>           # frontmatter properties
symdesk backlinks <file>       # incoming wikilinks
symdesk graph                  # nodes + edges for the link graph
symdesk views ...              # saved database views
symdesk ingest <file>          # copy a document into inbox/ + create a stub note
symdesk ask "question?"        # AI answer grounded in vault search results
symdesk meeting import <id>    # import a reviewed SymMeet meeting as a vault note (runtime-only, optional)
symdesk meeting list           # list imported meeting notes
symdesk meeting show <file>    # show one imported meeting note
symdesk meeting refresh <file> [--apply]  # preview (or apply) a transcript re-export
symdesk events --json          # NDJSON change stream (used by the app)
symdesk recipe validate .symdesk/recipes/daily.yml  # validate automation without running it
symdesk recipe run .symdesk/recipes/daily.yml       # stage runner proposals for review
symdesk recipe diff <run-id>                         # inspect proposed files
symdesk recipe accept <run-id>                       # apply an approved proposal
symdesk mcp                    # stdio MCP server for agents
symdesk serve                  # authenticated self-hosted API + durable queue
symdesk worker --server ...    # remote Tesseract/Ollama OCR worker
symdesk doctor                 # health check
symdesk version --json         # {"tool":"symdesk","version":...,"schema_version":1}

## Document workflow (vault contract v2)
symdesk docs list --type invoice          # list indexed documents, with filters
symdesk docs review                       # list documents needing review (low-confidence / missing metadata)
symdesk doc status <file> paid            # set document status (open|paid|submitted|done|...)
symdesk doc due <file> 2026-12-31        # set document due date (ISO-8601)
symdesk similar <file>                    # find near-duplicate documents by SimHash
symdesk demo init [dir]                   # materialise the built-in demo vault
symdesk demo init --size large [dir]      # materialise a deterministic 10k-document benchmark vault

All commands support --json for machine-readable output.

Large-vault performance baseline

Run make benchmark-large on a quiet machine to measure one complete index and search pass over the deterministic 10,000-document fixture. Record the result with the machine model and Go version before comparing changes; the target is intended for explicit performance checks, not routine CI. The core budget is a typical search below 100 ms after indexing. App measurements (grid scrolling, graph render, and viewer-open time) remain manual because they depend on the host GPU and the native app runtime.

Baseline (2026-07-11): Apple M4 Pro, Go benchmark harness, one index-and-search pass: 3.44 s (BenchmarkLargeVaultIndexAndSearch, -benchtime=1x).

Baseline (2026-07-19): Apple M4 Pro, Go 1.26.5, one symdesk graph pass over the same fixture with Memory entities present: 488 ms (BenchmarkGraphLargeVaultWithEntities, -benchtime=1x). Previously this scaled with entities × documents because each entity re-parsed every vault file from disk; the graph endpoint now parses each file once per call and reuses it across all matched entities.

Reviewed recipes (optional)

Recipes live in .symdesk/recipes/*.yml. They declare an allowed trigger, an explicit tool allow-list, and a hard write_cap. symdesk delegates execution to an optional symvibe runtime through a versioned JSON request/response contract; the runtime can only propose file contents. Each run is retained in .symdesk/runs/<id>/ as JSON plus a readable Markdown trace. No proposal changes the vault until symdesk recipe accept <id> is invoked; rejected runs remain available for inspection.

AI (optional, local-first or cloud)

symdesk ask and symdesk transform support both local models via Ollama and cloud providers (Anthropic Claude). If no provider is configured, the system degrades honestly by explaining the missing configuration and returning search results.

Local AI (Ollama)

To use a local model, configure the Ollama endpoint and model name:

export SYMDESK_LLM_PROVIDER=ollama      # default
export SYMDESK_OLLAMA_URL=http://localhost:11434
export SYMDESK_OLLAMA_MODEL=llama3.2     # optional, default llama3.2

The answer language is selected from the query by default. Set language in the config file or use SYMDESK_LANG to force a language for both ask and transform:

export SYMDESK_LANG=English

Cloud AI (Anthropic Claude)

To use Anthropic's Claude models, set the provider, model, and API key:

export SYMDESK_LLM_PROVIDER=anthropic
export SYMDESK_LLM_MODEL=claude-sonnet-5  # optional, default claude-sonnet-5

Anthropic output is limited to max_tokens = 8192 by default. Override it in the config file or with SYMDESK_MAX_TOKENS. If Anthropic stops at that limit, the stream ends with an explicit output-truncated notice instead of silently cutting off.

The API key (SYMDESK_LLM_API_KEY) is resolved dynamically in priority order:

  1. Environment Variable: Directly set SYMDESK_LLM_API_KEY.
  2. Symvault (1Password): If symvault is installed on your PATH, you can set the key to a symvault secret reference:
    export SYMDESK_LLM_API_KEY="op://vault/item/llm-api-key"
  3. macOS Keychain Fallback: If no key is set in the environment, symdesk checks the macOS Keychain for a password under service symaira-desktop and account llm-api-key. You can store it using:
    security add-generic-password -s symaira-desktop -a llm-api-key -w "your-api-key-here"

About

Local-first Markdown vault workspace combining CLI, MCP, and native macOS tools for people and agents.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Used by

Contributors

Languages