Update docs

This commit is contained in:
Andras Schmelczer 2025-11-22 12:13:22 +00:00
parent 38810579ec
commit 00d2061627
20 changed files with 1149 additions and 569 deletions

View file

@ -42,6 +42,11 @@ jobs:
cd docs
npm ci
- name: Check formatting
run: |
cd docs
npm run format:check
- name: Build documentation
run: |
cd docs

4
docs/.prettierignore Normal file
View file

@ -0,0 +1,4 @@
node_modules/
.vitepress/dist/
.vitepress/cache/
package-lock.json

19
docs/.prettierrc Normal file
View file

@ -0,0 +1,19 @@
{
"printWidth": 120,
"tabWidth": 4,
"useTabs": true,
"semi": false,
"singleQuote": false,
"trailingComma": "none",
"endOfLine": "lf",
"proseWrap": "preserve",
"overrides": [
{
"files": "*.md",
"options": {
"proseWrap": "preserve",
"printWidth": 120
}
}
]
}

View file

@ -1,62 +1,59 @@
import { defineConfig } from 'vitepress'
import { defineConfig } from "vitepress"
export default defineConfig({
title: 'VaultLink',
description: 'Self-hosted real-time synchronization for Obsidian',
base: '/vault-link/',
themeConfig: {
logo: '/logo.svg',
nav: [
{ text: 'Home', link: '/' },
{ text: 'Guide', link: '/guide/getting-started' },
{ text: 'Architecture', link: '/architecture/' },
{ text: 'GitHub', link: 'https://github.com/schmelczer/vault-link' }
],
sidebar: [
{
text: 'Introduction',
items: [
{ text: 'What is VaultLink?', link: '/guide/what-is-vaultlink' },
{ text: 'Getting Started', link: '/guide/getting-started' }
]
},
{
text: 'Setup',
items: [
{ text: 'Server Setup', link: '/guide/server-setup' },
{ text: 'Obsidian Plugin', link: '/guide/obsidian-plugin' },
{ text: 'CLI Client', link: '/guide/cli-client' }
]
},
{
text: 'Configuration',
items: [
{ text: 'Server Configuration', link: '/config/server' },
{ text: 'Authentication', link: '/config/authentication' },
{ text: 'Advanced Options', link: '/config/advanced' }
]
},
{
text: 'Architecture',
items: [
{ text: 'Overview', link: '/architecture/' },
{ text: 'Sync Algorithm', link: '/architecture/sync-algorithm' },
{ text: 'Data Flow', link: '/architecture/data-flow' }
]
}
],
socialLinks: [
{ icon: 'github', link: 'https://github.com/schmelczer/vault-link' }
],
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2024-present Andras Schmelczer'
},
search: {
provider: 'local'
}
},
head: [
['link', { rel: 'icon', type: 'image/svg+xml', href: '/vault-link/logo.svg' }]
]
title: "VaultLink",
description: "Self-hosted real-time synchronization for Obsidian",
base: "/vault-link/",
themeConfig: {
logo: "/logo.svg",
nav: [
{ text: "Home", link: "/" },
{ text: "Guide", link: "/guide/getting-started" },
{ text: "Architecture", link: "/architecture/" },
{ text: "GitHub", link: "https://github.com/schmelczer/vault-link" }
],
sidebar: [
{
text: "Introduction",
items: [
{ text: "What is VaultLink?", link: "/guide/what-is-vaultlink" },
{ text: "Getting Started", link: "/guide/getting-started" },
{ text: "Comparison with Alternatives", link: "/guide/alternatives" }
]
},
{
text: "Setup",
items: [
{ text: "Server Setup", link: "/guide/server-setup" },
{ text: "Obsidian Plugin", link: "/guide/obsidian-plugin" },
{ text: "CLI Client", link: "/guide/cli-client" }
]
},
{
text: "Configuration",
items: [
{ text: "Server Configuration", link: "/config/server" },
{ text: "Authentication", link: "/config/authentication" },
{ text: "Advanced Options", link: "/config/advanced" }
]
},
{
text: "Architecture",
items: [
{ text: "Overview", link: "/architecture/" },
{ text: "Sync Algorithm", link: "/architecture/sync-algorithm" },
{ text: "Data Flow", link: "/architecture/data-flow" }
]
}
],
socialLinks: [{ icon: "github", link: "https://github.com/schmelczer/vault-link" }],
footer: {
message: "Released under the MIT License.",
copyright: "Copyright © 2024-present Andras Schmelczer"
},
search: {
provider: "local"
}
},
head: [["link", { rel: "icon", type: "image/svg+xml", href: "/vault-link/logo.svg" }]]
})

View file

@ -44,6 +44,20 @@ Preview the built site:
npm run preview
```
### Format
Format all markdown and TypeScript files:
```bash
npm run format
```
Check formatting without making changes:
```bash
npm run format:check
```
## Deployment
The documentation is automatically deployed to GitHub Pages when changes are pushed to the `main` branch.
@ -81,6 +95,7 @@ docs/
### Markdown Features
VitePress supports:
- GitHub Flavored Markdown
- Custom containers (tip, warning, danger)
- Code syntax highlighting
@ -112,7 +127,7 @@ npm install
```yaml
server:
port: 3000
port: 3000
```
````

View file

@ -22,6 +22,7 @@ sequenceDiagram
```
**Steps**:
1. Client initiates WebSocket connection to server
2. Server accepts connection
3. Client sends authentication message with token and vault name
@ -72,6 +73,7 @@ sequenceDiagram
```
**Process**:
1. Client scans local filesystem
2. Client requests file list from server
3. Server queries database and returns metadata
@ -106,6 +108,7 @@ sequenceDiagram
```
**Flow**:
1. Filesystem watcher detects local change
2. Client reads file content
3. Client uploads file via WebSocket
@ -325,6 +328,7 @@ CREATE TABLE cursors (
### Queries
**Get files since version**:
```sql
SELECT * FROM documents
WHERE version > ? AND deleted = FALSE
@ -332,6 +336,7 @@ ORDER BY version ASC;
```
**Store new version**:
```sql
INSERT INTO versions (document_id, version, content, created_at)
VALUES (?, ?, ?, ?);
@ -342,6 +347,7 @@ WHERE id = ?;
```
**Update cursor**:
```sql
INSERT OR REPLACE INTO cursors (client_id, last_version, last_updated)
VALUES (?, ?, ?);
@ -352,87 +358,96 @@ VALUES (?, ?, ?);
### Client → Server Messages
**Upload File**:
```json
{
"type": "upload_file",
"path": "notes/example.md",
"content": "File content here...",
"base_version": 10,
"timestamp": "2024-01-01T12:00:00Z"
"type": "upload_file",
"path": "notes/example.md",
"content": "File content here...",
"base_version": 10,
"timestamp": "2024-01-01T12:00:00Z"
}
```
**Download File**:
```json
{
"type": "download_file",
"path": "notes/example.md"
"type": "download_file",
"path": "notes/example.md"
}
```
**Delete File**:
```json
{
"type": "delete_file",
"path": "notes/old.md"
"type": "delete_file",
"path": "notes/old.md"
}
```
**List Files**:
```json
{
"type": "list_files",
"since_version": 0
"type": "list_files",
"since_version": 0
}
```
### Server → Client Messages
**File Updated**:
```json
{
"type": "file_updated",
"path": "notes/example.md",
"version": 11,
"size": 1024,
"hash": "abc123..."
"type": "file_updated",
"path": "notes/example.md",
"version": 11,
"size": 1024,
"hash": "abc123..."
}
```
**File Content**:
```json
{
"type": "file_content",
"path": "notes/example.md",
"content": "Updated content...",
"version": 11
"type": "file_content",
"path": "notes/example.md",
"content": "Updated content...",
"version": 11
}
```
**File Deleted**:
```json
{
"type": "file_deleted",
"path": "notes/old.md",
"version": 12
"type": "file_deleted",
"path": "notes/old.md",
"version": 12
}
```
**Sync Complete**:
```json
{
"type": "sync_complete",
"total_files": 150,
"current_version": 200
"type": "sync_complete",
"total_files": 150,
"current_version": 200
}
```
**Error**:
```json
{
"type": "error",
"message": "File too large",
"code": "FILE_TOO_LARGE"
"type": "error",
"message": "File too large",
"code": "FILE_TOO_LARGE"
}
```
@ -441,18 +456,21 @@ VALUES (?, ?, ?);
### Client-Side Errors
**Network failure**:
1. Detect WebSocket disconnect
2. Queue pending operations
3. Retry connection with exponential backoff
4. Replay queued operations on reconnect
**File read error**:
1. Log error
2. Skip file
3. Continue with other files
4. Report to user
**Write conflict**:
1. Receive updated version from server
2. Apply OT merge locally
3. Overwrite local file
@ -461,16 +479,19 @@ VALUES (?, ?, ?);
### Server-Side Errors
**Database error**:
1. Log error
2. Return error to client
3. Client retries operation
**Invalid operation**:
1. Validate message format
2. Return specific error code
3. Client handles error appropriately
**Authentication failure**:
1. Reject connection
2. Send auth error
3. Client prompts for new credentials

View file

@ -43,6 +43,7 @@ VaultLink is built as a distributed system with a central sync server and multip
The central authority for synchronization, written in Rust using Axum framework.
**Responsibilities**:
- Accept WebSocket connections from clients
- Authenticate users via token-based auth
- Store document versions in SQLite
@ -51,6 +52,7 @@ The central authority for synchronization, written in Rust using Axum framework.
- Manage vault access control
**Technology**:
- **Language**: Rust 1.89+
- **Framework**: Axum (async web framework)
- **Database**: SQLite with SQLx
@ -62,6 +64,7 @@ The central authority for synchronization, written in Rust using Axum framework.
TypeScript library providing core synchronization logic, used by both the Obsidian plugin and CLI client.
**Responsibilities**:
- Manage WebSocket connection to server
- Watch local filesystem for changes
- Upload and download files
@ -70,6 +73,7 @@ TypeScript library providing core synchronization logic, used by both the Obsidi
- Maintain sync metadata
**Technology**:
- **Language**: TypeScript
- **Build**: Webpack
- **Protocol**: WebSocket client
@ -80,12 +84,14 @@ TypeScript library providing core synchronization logic, used by both the Obsidi
Integration layer between sync client and Obsidian.
**Responsibilities**:
- Provide UI for configuration
- Bridge sync client with Obsidian's file system API
- Handle Obsidian lifecycle events
- Display sync status to users
**Technology**:
- **Platform**: Obsidian Plugin API
- **Core**: sync-client library
- **UI**: Obsidian settings UI
@ -95,12 +101,14 @@ Integration layer between sync client and Obsidian.
Standalone executable for syncing vaults without Obsidian.
**Responsibilities**:
- Command-line interface
- File system access via Node.js
- Daemon mode for continuous sync
- Health check endpoint for monitoring
**Technology**:
- **Language**: TypeScript
- **Runtime**: Node.js
- **CLI**: Commander.js
@ -190,6 +198,7 @@ databases/
```
**Database Schema** (simplified):
- **documents**: File metadata (path, size, modified time)
- **versions**: Document content with version history
- **cursors**: Client sync state
@ -213,6 +222,7 @@ The `.vaultlink` directory tracks which files have been synced and their version
Client-server communication uses JSON messages over WebSocket.
**Message Types**:
- `upload_file`: Client → Server (file upload)
- `download_file`: Client → Server (request file)
- `file_updated`: Server → Client (file changed notification)
@ -253,11 +263,13 @@ Token-based authentication on connection:
### Scaling Approaches
**Vertical Scaling**:
- Increase server resources (CPU, RAM, storage)
- Optimize database queries and indexing
- Tune connection limits
**Horizontal Scaling** (future):
- Separate vault servers (vault sharding)
- Load balancer with sticky sessions
- Shared storage layer for SQLite databases

View file

@ -9,11 +9,13 @@ Operational transformation is a technique for managing concurrent edits to the s
### Why OT?
Traditional conflict resolution approaches:
- **Last write wins**: Loses data, frustrating for users
- **Manual merging**: Interrupts workflow, requires user intervention
- **Version branching**: Complex, not suitable for real-time sync
Operational transformation:
- **Automatic**: No user intervention required
- **Preserves all edits**: No data loss
- **Real-time**: Changes appear immediately
@ -23,6 +25,39 @@ Operational transformation:
VaultLink uses the [`reconcile-text`](https://crates.io/crates/reconcile-text) Rust library for operational transformation on text documents.
### Why reconcile-text over CRDTs?
VaultLink faces a **differential synchronization** challenge: users edit Obsidian vaults with various editors (Obsidian desktop, Obsidian mobile, Vim, VS Code, or any text editor), often while offline. This means we only observe the **final state** of each document after editing, not the individual keystrokes or operations that produced it.
**The fundamental problem**:
- **CRDTs and traditional OT** require capturing every individual operation (each character insertion, deletion, cursor movement)
- **VaultLink's reality**: Users edit files with arbitrary tools, sync happens after the fact
- **What we know**: Parent version and two modified versions
- **What we don't know**: The sequence of operations that created those modifications
**Why reconcile-text wins for this use case**:
1. **Works with end states only**: reconcile-text performs conflict-free 3-way merging given just parent, left, and right versions—no operation history needed
2. **Editor-agnostic**: Users can edit with any tool without requiring VaultLink-specific plugins or operation tracking
3. **Offline-first**: Edits made while disconnected are merged cleanly when sync resumes, because we're diffing final states rather than replaying operations
4. **No conflict markers**: Unlike Git merge, produces clean merged output without `<<<<<<<` markers that interrupt note-taking flow
5. **Human text forgiveness**: For knowledge bases and documentation, a slightly imperfect merge (e.g., minor word order issues) is vastly preferable to manual conflict resolution
6. **Simpler infrastructure**: No need for complex operation capture, transformation logs, or tombstone management that CRDTs require
**The tradeoff**:
CRDTs excel when you control the entire editing infrastructure and can capture every operation. reconcile-text excels when you're synchronizing independently-edited files—exactly VaultLink's scenario. The merge quality depends on Myers' diff algorithm rather than operation history, which is the correct tradeoff for differential sync.
For note-taking workflows where users value editor freedom and offline editing, this approach provides superior user experience compared to either CRDTs (which would require operation tracking) or Git-style merging (which requires manual conflict resolution).
[Learn more about reconcile-text →](https://schmelczer.dev/reconcile)
### How It Works
Given a base document and two sets of changes, OT produces a merged result that includes both changes.
@ -41,6 +76,7 @@ OT result: "Hello beautiful world!" (both changes applied)
### Operation Types
The algorithm handles these operations:
- **Insert**: Add text at position
- **Delete**: Remove text from position
- **Retain**: Keep existing text unchanged
@ -62,10 +98,12 @@ VaultLink maintains sync state to track which changes have been applied.
### Version Vectors
Each document has a version tracked by:
- **Server version**: Incremented on each change
- **Client cursors**: Track which version each client has seen
This enables:
- Efficient syncing (only send changes since last sync)
- Conflict detection (concurrent edits to same version)
- Ordering of operations
@ -84,6 +122,7 @@ struct Cursor {
```
On sync:
1. Client sends cursor (last seen version)
2. Server returns all changes since that version
3. Client applies changes and updates cursor
@ -95,42 +134,47 @@ On sync:
Two users edit the same paragraph simultaneously.
**Initial state**:
```
Version 10: "The quick brown fox jumps over the lazy dog."
```
**User A's edit** (version 11):
```
"The quick brown fox jumps over the very lazy dog."
```
*Inserts "very " at position 40*
_Inserts "very " at position 40_
**User B's edit** (also from version 10):
```
"The quick red fox jumps over the lazy dog."
```
*Replaces "brown" with "red" at position 10*
_Replaces "brown" with "red" at position 10_
### Server Processing
1. **Receive User A's operation**:
- Base: version 10
- Operation: Insert("very ", position=40)
- Apply to database → version 11
- Base: version 10
- Operation: Insert("very ", position=40)
- Apply to database → version 11
2. **Receive User B's operation**:
- Base: version 10
- Operation: Replace("brown"→"red", position=10)
- **Conflict detected**: Base is version 10, but current is version 11
- Base: version 10
- Operation: Replace("brown"→"red", position=10)
- **Conflict detected**: Base is version 10, but current is version 11
3. **Transform User B's operation**:
- Transform against User A's operation
- Adjust positions/content as needed
- Apply transformed operation → version 12
- Transform against User A's operation
- Adjust positions/content as needed
- Apply transformed operation → version 12
4. **Broadcast updates**:
- Send User A's operation to User B
- Send transformed User B's operation to User A
- Send User A's operation to User B
- Send transformed User B's operation to User A
### Final Result
@ -147,11 +191,13 @@ Both edits are preserved in the final document.
**Scenario**: User A deletes a paragraph while User B edits it.
**Resolution**:
- OT algorithm prioritizes preservation of content
- Insert operation is transformed to account for deletion
- Typically results in inserted content appearing nearby
**Example**:
```
Base: "Line 1\nLine 2\nLine 3"
@ -160,6 +206,7 @@ User B: Edit Line 2 → "Line 1\nLine 2 modified\nLine 3"
Result: "Line 1\nLine 2 modified\nLine 3"
```
(Insert takes precedence, preserving user content)
### 2. Overlapping Edits
@ -167,6 +214,7 @@ Result: "Line 1\nLine 2 modified\nLine 3"
**Scenario**: Two users edit overlapping regions.
**Resolution**:
- OT splits operations into non-overlapping segments
- Applies each segment independently
- Merges results
@ -176,6 +224,7 @@ Result: "Line 1\nLine 2 modified\nLine 3"
**Scenario**: Two users delete overlapping text.
**Resolution**:
- Deletes are merged
- Final result has the union of deleted ranges removed
@ -184,6 +233,7 @@ Result: "Line 1\nLine 2 modified\nLine 3"
**Scenario**: Client loses connection, makes edits offline, reconnects.
**Resolution**:
1. Client queues edits locally
2. On reconnect, sends all queued operations
3. Server applies OT against all operations that happened during partition
@ -206,6 +256,7 @@ Result: "Line 1\nLine 2 modified\nLine 3"
### Optimization
VaultLink optimizes for:
- Small, frequent edits (typical typing patterns)
- Text documents (not binary files)
- Real-time processing (no batching delay)
@ -215,6 +266,7 @@ VaultLink optimizes for:
### Binary Files
OT works best for text files. Binary files:
- Cannot be meaningfully merged
- Use last-write-wins strategy
- May cause data loss on concurrent edits
@ -224,6 +276,7 @@ OT works best for text files. Binary files:
### Large Documents
Very large documents (> 1MB) may have:
- Higher transformation costs
- Slower sync times
- Increased memory usage
@ -233,6 +286,7 @@ Very large documents (> 1MB) may have:
### Complex Formatting
Markdown with complex structures may occasionally produce unexpected results:
- Nested lists
- Tables
- Code blocks
@ -244,6 +298,7 @@ Markdown with complex structures may occasionally produce unexpected results:
### Strong Consistency
VaultLink provides **strong eventual consistency**:
- All clients eventually converge to the same state
- Operations applied in causal order
- No data loss under normal operation
@ -264,32 +319,36 @@ VaultLink provides **strong eventual consistency**:
### Git-style Merging
| Aspect | Git Merge | VaultLink OT |
|--------|-----------|--------------|
| Real-time | No | Yes |
| Manual conflict resolution | Yes | No |
| Branching | Yes | No |
| Automatic merge | Limited | Always |
| Use case | Code changes | Collaborative documents |
| Aspect | Git Merge | VaultLink OT |
| -------------------------- | ------------ | ----------------------- |
| Real-time | No | Yes |
| Manual conflict resolution | Yes | No |
| Branching | Yes | No |
| Automatic merge | Limited | Always |
| Use case | Code changes | Collaborative documents |
### CRDTs (Conflict-free Replicated Data Types)
| Aspect | CRDTs | VaultLink OT |
|--------|-------|--------------|
| Server required | No | Yes |
| Memory overhead | Higher | Lower |
| Complexity | Higher | Lower |
| Deletion handling | Complex (tombstones) | Simple |
| Best for | Distributed systems | Centralized sync |
| Aspect | CRDTs | VaultLink (reconcile-text) |
| ----------------------------- | ------------------------------------ | ------------------------------------------------- |
| **Operation tracking** | Required (every keystroke) | Not required (end states only) |
| **Editor freedom** | Limited (must use CRDT-aware editor) | Unlimited (any text editor works) |
| **Offline editing** | Requires operation log | Works with file comparison |
| **Server required** | No | Yes |
| **Memory overhead** | Higher (tombstones, metadata) | Lower (versions only) |
| **Infrastructure complexity** | Higher | Lower |
| **Best for** | Controlled editing environments | Independent file editing (Obsidian, Vim, VS Code) |
**Key insight**: CRDTs are superior when you can capture every operation. reconcile-text is superior when users edit files independently with arbitrary tools—exactly VaultLink's scenario.
### Last Write Wins
| Aspect | LWW | VaultLink OT |
|--------|-----|--------------|
| Data loss | Yes | No |
| Simplicity | High | Medium |
| User experience | Poor | Excellent |
| Performance | Best | Good |
| Aspect | LWW | VaultLink OT |
| --------------- | ---- | ------------ |
| Data loss | Yes | No |
| Simplicity | High | Medium |
| User experience | Poor | Excellent |
| Performance | Best | Good |
## Algorithm Details
@ -298,20 +357,20 @@ VaultLink provides **strong eventual consistency**:
When transforming operation `A` against operation `B`:
1. **Insert vs Insert**:
- If positions equal: Order by client ID
- If different positions: Adjust positions
- If positions equal: Order by client ID
- If different positions: Adjust positions
2. **Insert vs Delete**:
- If insert in deleted range: Shift insert position
- If insert after delete: Adjust position by deleted length
- If insert in deleted range: Shift insert position
- If insert after delete: Adjust position by deleted length
3. **Delete vs Delete**:
- If ranges overlap: Merge delete ranges
- If ranges disjoint: Adjust positions
- If ranges overlap: Merge delete ranges
- If ranges disjoint: Adjust positions
4. **Retain vs Any**:
- Retain operations don't conflict
- Simply adjust positions
- Retain operations don't conflict
- Simply adjust positions
### Transformation Example

View file

@ -13,11 +13,13 @@ While VaultLink handles most SQLite configuration automatically, you can optimiz
VaultLink uses Write-Ahead Logging (WAL) mode by default for better concurrency.
**Benefits**:
- Readers don't block writers
- Writers don't block readers
- Better performance for concurrent access
**Maintenance**:
```bash
# Checkpoint WAL to main database (run periodically)
sqlite3 databases/vault.db "PRAGMA wal_checkpoint(TRUNCATE);"
@ -39,6 +41,7 @@ sqlite3 databases/vault.db "ANALYZE;"
```
**Schedule maintenance**:
```bash
#!/bin/bash
# monthly-maintenance.sh
@ -83,6 +86,7 @@ max_connections = (concurrent_users × avg_operations_per_user) + buffer
```
**Example**:
- 20 concurrent users
- 2 operations per user on average
- 25% buffer
@ -96,30 +100,33 @@ max_connections = (20 × 2) × 1.25 = 50
Adjust timeouts based on network characteristics:
**Fast local network**:
```yaml
database:
cursor_timeout_seconds: 30
cursor_timeout_seconds: 30
server:
response_timeout_seconds: 30
response_timeout_seconds: 30
```
**Slow or unreliable network**:
```yaml
database:
cursor_timeout_seconds: 180
cursor_timeout_seconds: 180
server:
response_timeout_seconds: 120
response_timeout_seconds: 120
```
**Mobile clients**:
```yaml
database:
cursor_timeout_seconds: 300 # Longer for intermittent connections
cursor_timeout_seconds: 300 # Longer for intermittent connections
server:
response_timeout_seconds: 180
response_timeout_seconds: 180
```
## Reverse Proxy Configuration
@ -232,16 +239,16 @@ Using Docker labels:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultlink.rule=Host(`sync.example.com`)"
- "traefik.http.routers.vaultlink.entrypoints=websecure"
- "traefik.http.routers.vaultlink.tls.certresolver=letsencrypt"
- "traefik.http.services.vaultlink.loadbalancer.server.port=3000"
# Middleware for timeouts
- "traefik.http.middlewares.vaultlink-timeout.timeout.request=3600s"
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.vaultlink.rule=Host(`sync.example.com`)"
- "traefik.http.routers.vaultlink.entrypoints=websecure"
- "traefik.http.routers.vaultlink.tls.certresolver=letsencrypt"
- "traefik.http.services.vaultlink.loadbalancer.server.port=3000"
# Middleware for timeouts
- "traefik.http.middlewares.vaultlink-timeout.timeout.request=3600s"
```
## Docker Optimizations
@ -252,16 +259,16 @@ Limit container resources:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
deploy:
resources:
limits:
cpus: '2.0'
memory: 4G
reservations:
cpus: '1.0'
memory: 2G
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
deploy:
resources:
limits:
cpus: "2.0"
memory: 4G
reservations:
cpus: "1.0"
memory: 2G
```
### Logging Configuration
@ -270,13 +277,13 @@ Optimize Docker logging:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "5"
```
### Volume Optimization
@ -285,21 +292,21 @@ Use named volumes for better performance:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
volumes:
- vaultlink-data:/data
- vaultlink-logs:/data/logs
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
volumes:
- vaultlink-data:/data
- vaultlink-logs:/data/logs
volumes:
vaultlink-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/fast-ssd/vaultlink
vaultlink-logs:
driver: local
vaultlink-data:
driver: local
driver_opts:
type: none
o: bind
device: /mnt/fast-ssd/vaultlink
vaultlink-logs:
driver: local
```
## High Availability
@ -310,14 +317,14 @@ Comprehensive health monitoring:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/vaults/health/ping || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/vaults/health/ping || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
```
Monitor health in production:
@ -375,6 +382,7 @@ find "$BACKUP_DIR" -name "vaultlink-*.tar.gz" -mtime +$RETENTION_DAYS -delete
```
Schedule with cron:
```cron
0 2 * * * /opt/vaultlink/backup-vaultlink.sh
```
@ -424,21 +432,21 @@ While VaultLink doesn't expose metrics natively, monitor Docker:
```yaml
# docker-compose.yml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=3000"
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=3000"
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- 8080:8080
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- 8080:8080
```
### Log Analysis
@ -484,17 +492,17 @@ Run VaultLink in isolated network:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
networks:
- vaultlink-internal
- proxy-external
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
networks:
- vaultlink-internal
- proxy-external
networks:
vaultlink-internal:
internal: true
proxy-external:
driver: bridge
vaultlink-internal:
internal: true
proxy-external:
driver: bridge
```
### Read-Only Root Filesystem
@ -503,12 +511,12 @@ Run with read-only root (mount writable volumes for data):
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
read_only: true
volumes:
- ./data:/data
- /tmp
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
read_only: true
volumes:
- ./data:/data
- /tmp
```
### Drop Capabilities
@ -517,12 +525,12 @@ Run with minimal privileges:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
```
## Migration
@ -530,19 +538,22 @@ services:
### Moving to New Server
1. **Backup on old server**:
```bash
./backup-vaultlink.sh
```
```bash
./backup-vaultlink.sh
```
2. **Transfer backup**:
```bash
scp vaultlink-backup.tar.gz new-server:/tmp/
```
```bash
scp vaultlink-backup.tar.gz new-server:/tmp/
```
3. **Restore on new server**:
```bash
./restore-vaultlink.sh /tmp/vaultlink-backup.tar.gz
```
```bash
./restore-vaultlink.sh /tmp/vaultlink-backup.tar.gz
```
4. **Update DNS/clients** to point to new server

View file

@ -5,6 +5,7 @@ VaultLink uses token-based authentication with per-user vault access control. Th
## Overview
Authentication in VaultLink:
- **Token-based**: Users authenticate with secure tokens
- **Configured in YAML**: All users defined in `config.yml`
- **Vault-level access**: Control which vaults each user can access
@ -14,11 +15,11 @@ Authentication in VaultLink:
```yaml
users:
user_configs:
- name: alice
token: alice-secure-token-here
vault_access:
type: allow_access_to_all
user_configs:
- name: alice
token: alice-secure-token-here
vault_access:
type: allow_access_to_all
```
## User Configuration Fields
@ -35,6 +36,7 @@ Human-readable identifier for the user. Used in logs and auditing.
```
**Notes**:
- Must be unique across all users
- Used for identification only, not authentication
- Appears in server logs
@ -52,6 +54,7 @@ Authentication token for the user. Must be kept secret.
```
**Best practices**:
- Generate with: `openssl rand -hex 32`
- Minimum length: 32 characters
- Use different token per user
@ -59,6 +62,7 @@ Authentication token for the user. Must be kept secret.
- Rotate periodically
**Example token generation**:
```bash
# Generate a secure token
openssl rand -hex 32
@ -73,6 +77,7 @@ openssl rand -hex 32
Defines which vaults the user can access.
**Three modes**:
1. `allow_access_to_all`: Access to all vaults
2. `allow_list`: Access to specific vaults only
3. `deny_list`: Access to all vaults except specific ones
@ -85,14 +90,15 @@ Grant access to every vault:
```yaml
users:
user_configs:
- name: admin
token: admin-token
vault_access:
type: allow_access_to_all
user_configs:
- name: admin
token: admin-token
vault_access:
type: allow_access_to_all
```
**Use cases**:
- Administrator accounts
- Personal single-user deployments
- Development/testing
@ -103,23 +109,25 @@ Grant access only to specific vaults:
```yaml
users:
user_configs:
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal
- shared-team
- project-alpha
user_configs:
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal
- shared-team
- project-alpha
```
**Use cases**:
- Multi-user deployments
- Restricted access scenarios
- Separation of concerns
**Notes**:
- User can only access listed vaults
- Attempting to access other vaults returns authentication error
- Empty list = no access to any vault
@ -130,21 +138,23 @@ Grant access to all vaults except specific ones:
```yaml
users:
user_configs:
- name: bob
token: bob-token
vault_access:
type: deny_list
denied:
- restricted
- admin-only
user_configs:
- name: bob
token: bob-token
vault_access:
type: deny_list
denied:
- restricted
- admin-only
```
**Use cases**:
- Users with broad access except sensitive vaults
- Simplify configuration when most vaults are accessible
**Notes**:
- User can access any vault not in the deny list
- Attempting to access denied vaults returns authentication error
@ -154,75 +164,75 @@ users:
```yaml
users:
user_configs:
- name: me
token: my-super-secret-token
vault_access:
type: allow_access_to_all
user_configs:
- name: me
token: my-super-secret-token
vault_access:
type: allow_access_to_all
```
### Small Team (Shared Vaults)
```yaml
users:
user_configs:
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal-alice
- team-shared
- name: bob
token: bob-token
vault_access:
type: allow_list
allowed:
- personal-bob
- team-shared
- name: charlie
token: charlie-token
vault_access:
type: allow_list
allowed:
- personal-charlie
- team-shared
user_configs:
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal-alice
- team-shared
- name: bob
token: bob-token
vault_access:
type: allow_list
allowed:
- personal-bob
- team-shared
- name: charlie
token: charlie-token
vault_access:
type: allow_list
allowed:
- personal-charlie
- team-shared
```
### Organization (Mixed Access)
```yaml
users:
user_configs:
- name: admin
token: admin-token
vault_access:
type: allow_access_to_all
user_configs:
- name: admin
token: admin-token
vault_access:
type: allow_access_to_all
- name: developer
token: dev-token
vault_access:
type: allow_list
allowed:
- engineering-docs
- api-specs
- shared
- name: developer
token: dev-token
vault_access:
type: allow_list
allowed:
- engineering-docs
- api-specs
- shared
- name: designer
token: design-token
vault_access:
type: allow_list
allowed:
- design-docs
- brand-assets
- shared
- name: designer
token: design-token
vault_access:
type: allow_list
allowed:
- design-docs
- brand-assets
- shared
- name: readonly
token: readonly-token
vault_access:
type: allow_list
allowed:
- public-wiki
- name: readonly
token: readonly-token
vault_access:
type: allow_list
allowed:
- public-wiki
```
## Authentication Flow
@ -231,23 +241,24 @@ users:
1. Client connects via WebSocket
2. Client sends authentication message:
```json
{
"type": "auth",
"token": "user-token",
"vault": "vault-name"
}
```
```json
{
"type": "auth",
"token": "user-token",
"vault": "vault-name"
}
```
3. Server validates:
- Token exists in config
- User has access to requested vault
- Token exists in config
- User has access to requested vault
4. Server responds:
- Success: Connection established
- Failure: Connection closed with error
- Success: Connection established
- Failure: Connection closed with error
### Validation
Server checks:
1. **Token match**: Token exists in `user_configs`
2. **Vault access**: User has permission for vault
3. **Connection limits**: Not exceeding `max_clients_per_vault`
@ -255,16 +266,19 @@ Server checks:
### Errors
**Invalid token**:
```
Authentication failed: Invalid token
```
**No vault access**:
```
Authentication failed: User does not have access to vault 'restricted'
```
**Connection limit**:
```
Connection rejected: Maximum clients reached for vault
```
@ -289,14 +303,16 @@ uuidgen
### Token Storage
**In config file**:
```yaml
users:
user_configs:
- name: alice
token: !ENV ALICE_TOKEN # Read from environment variable
user_configs:
- name: alice
token: !ENV ALICE_TOKEN # Read from environment variable
```
**Load from environment**:
```bash
export ALICE_TOKEN="$(openssl rand -hex 32)"
./sync_server config.yml
@ -314,11 +330,13 @@ Periodically change tokens:
### Token Revocation
To revoke access:
1. Remove user from `config.yml`
2. Restart server
3. User's connections will be rejected
For immediate revocation:
- Remove user from config
- Restart server
- Existing connections are terminated
@ -354,6 +372,7 @@ Grant temporary access:
4. Restart server
For automation:
```bash
# Add user with expiry comment
echo " - name: temp-user # EXPIRES: 2024-12-31" >> config.yml
@ -363,6 +382,7 @@ echo " token: temp-token" >> config.yml
### Shared Tokens (Not Recommended)
Multiple users sharing a token:
- All appear as same user in logs
- Can't revoke individual access
- Security risk if one person leaves
@ -432,25 +452,25 @@ Tokens for automated systems:
```yaml
users:
user_configs:
- name: backup-service
token: backup-service-token
vault_access:
type: allow_access_to_all
user_configs:
- name: backup-service
token: backup-service-token
vault_access:
type: allow_access_to_all
- name: ci-pipeline
token: ci-token
vault_access:
type: allow_list
allowed:
- documentation
- name: ci-pipeline
token: ci-token
vault_access:
type: allow_list
allowed:
- documentation
- name: monitoring
token: monitoring-token
vault_access:
type: allow_list
allowed:
- metrics
- name: monitoring
token: monitoring-token
vault_access:
type: allow_list
allowed:
- metrics
```
### Dynamic Vault Access
@ -462,6 +482,7 @@ VaultLink doesn't support runtime user management. To change access:
3. Users reconnect automatically
For frequent changes, consider:
- Over-provision access (deny list)
- Use external authentication proxy
- Script config updates + reload
@ -471,18 +492,21 @@ For frequent changes, consider:
### Can't connect
**Check token**:
```bash
# Verify token in config matches client
grep "token:" config.yml
```
**Check vault name**:
```bash
# Ensure vault is in allowed list
grep -A 5 "name: alice" config.yml
```
**Check server logs**:
```bash
tail -f logs/*.log | grep -i auth
```
@ -490,18 +514,20 @@ tail -f logs/*.log | grep -i auth
### Access denied
**Verify vault access**:
```yaml
# Check user's vault_access configuration
users:
user_configs:
- name: alice
vault_access:
type: allow_list
allowed:
- vault-name # Must match exactly
user_configs:
- name: alice
vault_access:
type: allow_list
allowed:
- vault-name # Must match exactly
```
**Case sensitivity**:
- Vault names are case-sensitive
- `Vault``vault`
- Ensure exact match in config and client
@ -509,11 +535,13 @@ users:
### Token not working
**Check for typos**:
- Extra spaces
- Hidden characters
- Wrong quotes in YAML
**Regenerate token**:
```bash
# Generate new token
openssl rand -hex 32

View file

@ -14,40 +14,40 @@ The server is configured using a YAML file passed as a command-line argument:
```yaml
database:
databases_directory_path: databases
max_connections_per_vault: 12
cursor_timeout_seconds: 60
databases_directory_path: databases
max_connections_per_vault: 12
cursor_timeout_seconds: 60
server:
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 60
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 60
users:
user_configs:
- name: admin
token: your-secure-random-token
vault_access:
type: allow_access_to_all
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal
- shared
- name: bob
token: bob-token
vault_access:
type: deny_list
denied:
- restricted
user_configs:
- name: admin
token: your-secure-random-token
vault_access:
type: allow_access_to_all
- name: alice
token: alice-token
vault_access:
type: allow_list
allowed:
- personal
- shared
- name: bob
token: bob-token
vault_access:
type: deny_list
denied:
- restricted
logging:
log_directory: logs
log_rotation: 7days
log_directory: logs
log_rotation: 7days
```
## Database Section
@ -62,10 +62,11 @@ Directory where SQLite database files are stored. One database file per vault.
```yaml
database:
databases_directory_path: /data/databases
databases_directory_path: /data/databases
```
The directory structure:
```
databases/
├── vault-1.db
@ -74,6 +75,7 @@ databases/
```
**Notes**:
- Path is relative to working directory or absolute
- Directory must be writable by the server process
- Ensure adequate disk space for vault data
@ -90,10 +92,11 @@ Maximum concurrent database connections per vault.
```yaml
database:
max_connections_per_vault: 12
max_connections_per_vault: 12
```
**Tuning**:
- Higher values: Better performance under load
- Lower values: Less memory usage
- Typical range: 8-20
@ -110,10 +113,11 @@ How long to keep database cursors alive for inactive clients.
```yaml
database:
cursor_timeout_seconds: 60
cursor_timeout_seconds: 60
```
**Notes**:
- Cursors track client sync state
- Timeout too short: Clients may need to re-sync frequently
- Timeout too long: More memory usage
@ -139,6 +143,7 @@ server:
```
**Common values**:
- `0.0.0.0`: Listen on all network interfaces (production)
- `127.0.0.1`: Listen on localhost only (development/testing)
- Specific IP: Listen on specific interface
@ -154,10 +159,11 @@ TCP port to listen on.
```yaml
server:
port: 3000
port: 3000
```
**Notes**:
- Must be available (not in use)
- Privileged ports (< 1024) require root
- Common ports: 3000, 8080, 8888
@ -174,16 +180,18 @@ Maximum size of HTTP request body in megabytes.
```yaml
server:
max_body_size_mb: 512
max_body_size_mb: 512
```
**Usage**:
- Limits file upload size
- Prevents memory exhaustion attacks
- Must be larger than largest expected file
- Consider client `max_file_size_mb` settings
**Tuning**:
- Small vaults (mostly text): 100 MB
- Medium vaults (some images): 512 MB
- Large vaults (many images/PDFs): 1024+ MB
@ -199,16 +207,18 @@ Maximum concurrent clients per vault.
```yaml
server:
max_clients_per_vault: 256
max_clients_per_vault: 256
```
**Notes**:
- Limits concurrent WebSocket connections
- Prevents resource exhaustion
- Consider expected number of users
- Each client uses memory and file descriptors
**Scaling**:
- Personal use: 10-50
- Small team: 50-100
- Large team: 100-500
@ -224,15 +234,17 @@ Maximum time to wait for client responses.
```yaml
server:
response_timeout_seconds: 60
response_timeout_seconds: 60
```
**Usage**:
- Timeout for HTTP requests
- Timeout for WebSocket operations
- Clients disconnected if unresponsive
**Tuning**:
- Fast networks: 30 seconds
- Slow networks: 90-120 seconds
- Large file uploads: Increase proportionally
@ -259,6 +271,7 @@ logging:
```
**Notes**:
- Path is relative to working directory or absolute
- Directory must be writable
- Logs are rotated based on `log_rotation`
@ -284,10 +297,12 @@ logging:
**Format**: `<number><unit>`
**Units**:
- `hours`: Hours (e.g., `12hours`, `24hours`)
- `days`: Days (e.g., `7days`, `30days`)
**Recommendations**:
- Development: `24hours` or `7days`
- Production: `7days` or `30days`
- High traffic: `24hours` (logs can be large)
@ -298,55 +313,55 @@ logging:
```yaml
database:
databases_directory_path: ./databases
max_connections_per_vault: 8
cursor_timeout_seconds: 30
databases_directory_path: ./databases
max_connections_per_vault: 8
cursor_timeout_seconds: 30
server:
host: 127.0.0.1
port: 3000
max_body_size_mb: 100
max_clients_per_vault: 10
response_timeout_seconds: 30
host: 127.0.0.1
port: 3000
max_body_size_mb: 100
max_clients_per_vault: 10
response_timeout_seconds: 30
users:
user_configs:
- name: dev
token: dev-token
vault_access:
type: allow_access_to_all
user_configs:
- name: dev
token: dev-token
vault_access:
type: allow_access_to_all
logging:
log_directory: logs
log_rotation: 24hours
log_directory: logs
log_rotation: 24hours
```
### Production
```yaml
database:
databases_directory_path: /data/databases
max_connections_per_vault: 16
cursor_timeout_seconds: 120
databases_directory_path: /data/databases
max_connections_per_vault: 16
cursor_timeout_seconds: 120
server:
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 90
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 90
users:
user_configs:
- name: admin
token: <strong-random-token>
vault_access:
type: allow_access_to_all
# Additional users...
user_configs:
- name: admin
token: <strong-random-token>
vault_access:
type: allow_access_to_all
# Additional users...
logging:
log_directory: /data/logs
log_rotation: 7days
log_directory: /data/logs
log_rotation: 7days
```
## Validation
@ -362,6 +377,7 @@ tail -f logs/latest.log
```
**Common errors**:
- Missing required fields
- Invalid YAML syntax
- Invalid values (negative numbers, etc.)
@ -375,11 +391,11 @@ For many concurrent users:
```yaml
database:
max_connections_per_vault: 20 # Increase
max_connections_per_vault: 20 # Increase
server:
max_clients_per_vault: 500 # Increase
response_timeout_seconds: 120 # Increase for slow clients
max_clients_per_vault: 500 # Increase
response_timeout_seconds: 120 # Increase for slow clients
```
### Large Files
@ -388,8 +404,8 @@ For vaults with large files:
```yaml
server:
max_body_size_mb: 1024 # Allow larger uploads
response_timeout_seconds: 180 # More time for uploads
max_body_size_mb: 1024 # Allow larger uploads
response_timeout_seconds: 180 # More time for uploads
```
### Resource-Constrained Systems
@ -398,11 +414,11 @@ For limited CPU/memory:
```yaml
database:
max_connections_per_vault: 6 # Reduce
max_connections_per_vault: 6 # Reduce
server:
max_clients_per_vault: 50 # Reduce
max_body_size_mb: 256 # Reduce
max_clients_per_vault: 50 # Reduce
max_body_size_mb: 256 # Reduce
```
## Security Considerations
@ -431,12 +447,14 @@ server:
### Server won't start
**Check YAML syntax**:
```bash
# Use a YAML validator
python -c 'import yaml, sys; yaml.safe_load(open("config.yml"))'
```
**Check file paths**:
```bash
# Ensure directories exist and are writable
mkdir -p databases logs
@ -444,6 +462,7 @@ chmod 755 databases logs
```
**Check port availability**:
```bash
# Verify port is not in use
lsof -i :3000

324
docs/guide/alternatives.md Normal file
View file

@ -0,0 +1,324 @@
# Comparison with Alternatives
VaultLink is one of several solutions for synchronizing Obsidian vaults. This page compares VaultLink with popular alternatives to help you choose the right tool.
## Key Differentiator: Editor Agnostic
**VaultLink is not tied to Obsidian.** While it includes an Obsidian plugin for convenience, VaultLink synchronizes plain text files and works with any editor:
- Edit with **Obsidian desktop** on your laptop
- Edit with **Vim** on your server
- Edit with **VS Code** on your workstation
- Edit with **Obsidian mobile** on your phone
- Use the **CLI client** for automated workflows
All changes merge automatically without conflict markers, regardless of which editor you use. This is possible because VaultLink uses [reconcile-text](/architecture/sync-algorithm#why-reconcile-text-over-crdts) for differential synchronization rather than requiring operation-level tracking.
## VaultLink's Core Strengths
Before diving into comparisons:
1. **Fully self-hosted**: Server and all components are open source
2. **Collaborative editing**: Real-time sync with operational transformation
3. **Automatic conflict resolution**: No manual intervention or paid features required
4. **Cursor tracking**: See where other users are editing
5. **Extensively tested**: Comprehensive test suite for server and client
6. **Editor freedom**: Use any text editor, not just Obsidian
7. **Production-ready**: Docker images, health checks, monitoring
## Obsidian Sync Alternatives
### Self-hosted LiveSync
**Downloads**: ~300,000
**Repository**: https://github.com/vrtmrz/obsidian-livesync
**Overview**: CouchDB/IBM Cloudant-based sync with end-to-end encryption.
| Aspect | Self-hosted LiveSync | VaultLink |
| ------------------------- | --------------------------- | -------------------------------------- |
| **Self-hosted** | Yes (CouchDB required) | Yes (single binary or Docker) |
| **Conflict resolution** | Manual or automatic (basic) | Automatic (operational transformation) |
| **Collaborative editing** | No | Yes (real-time with cursors) |
| **Editor support** | Obsidian only | Any text editor |
| **Infrastructure** | CouchDB database | SQLite (bundled) |
| **Deployment complexity** | Medium (external DB) | Low (single container) |
| **End-to-end encryption** | Yes | No (transport encryption only) |
| **Out-of-band edits** | Limited support | Full support (edit with any tool) |
**When to use LiveSync**:
- Need end-to-end encryption
- Already running CouchDB
- Only use Obsidian (no external editors)
**When to use VaultLink**:
- Want collaborative editing with multiple users
- Edit files with various tools (Vim, VS Code, etc.)
- Need simpler deployment (no external database)
- Want operational transformation for better merges
---
### Remotely Save
**Downloads**: ~1.1M
**Repository**: https://github.com/remotely-save/remotely-save
**Overview**: Sync to cloud storage providers (S3, Dropbox, OneDrive, WebDAV).
| Aspect | Remotely Save | VaultLink |
| ------------------------- | ---------------------------- | ------------------------ |
| **Self-hosted** | Partial (uses cloud storage) | Fully self-hosted |
| **Conflict resolution** | Paid Pro feature | Free and automatic |
| **Collaborative editing** | No | Yes |
| **Editor support** | Obsidian only | Any text editor |
| **Storage backend** | Cloud providers | Self-hosted SQLite |
| **Cost** | Free (basic) / Paid (Pro) | Free (open source) |
| **Code quality** | No tests, complex codebase | Comprehensive test suite |
| **Real-time sync** | No (periodic polling) | Yes (WebSocket) |
**When to use Remotely Save**:
- Already use cloud storage (S3, Dropbox)
- Don't need real-time sync
- Single-user scenario
**When to use VaultLink**:
- Want full control over data
- Need automatic conflict resolution without paying
- Want real-time collaborative editing
- Value code quality and testing
**Note**: Remotely Save's conflict resolution is a paid feature. VaultLink provides superior automatic merging for free.
---
### Relay
**Downloads**: ~24,000
**Repository**: https://github.com/No-Instructions/Relay
**Overview**: CRDT-based sync with proprietary server component.
| Aspect | Relay | VaultLink |
| -------------------------- | ---------------------------- | ----------------------- |
| **Self-hosted** | No (proprietary server) | Yes (fully open source) |
| **Conflict resolution** | CRDT (automatic) | OT (automatic) |
| **Collaborative editing** | Yes | Yes |
| **Editor support** | Obsidian only | Any text editor |
| **Out-of-band edits** | No (breaks CRDT consistency) | Yes (differential sync) |
| **Server open source** | No | Yes |
| **Infrastructure control** | Limited | Full |
| **Per-file overhead** | High (CRDT metadata) | Low (version history) |
**When to use Relay**:
- Want hosted solution (don't self-host)
- Only edit within Obsidian
- Don't need out-of-band editing
**When to use VaultLink**:
- Need fully open source solution
- Want to self-host completely
- Edit files outside Obsidian (Vim, VS Code)
- Value infrastructure control
**Critical limitation**: Relay's CRDT approach requires tracking every operation within Obsidian. Editing files outside Obsidian breaks the CRDT state. VaultLink's differential sync works regardless of how files are edited.
---
### Obsidian Git
**Downloads**: ~1.4M
**Repository**: https://github.com/denolehov/obsidian-git
**Overview**: Uses Git for version control and synchronization.
| Aspect | Obsidian Git | VaultLink |
| ------------------------- | ----------------------------- | ----------------------- |
| **Self-hosted** | Yes (Git server) | Yes (sync server) |
| **Conflict resolution** | Manual (conflict markers) | Automatic (no markers) |
| **Collaborative editing** | No | Yes (real-time) |
| **Editor support** | Any (it's Git) | Any (differential sync) |
| **Version history** | Full Git history | Document versions |
| **Real-time sync** | No (commit-based) | Yes (instant) |
| **Merge conflicts** | Manual resolution | Automatic |
| **Learning curve** | High (Git knowledge required) | Low |
| **Workflow interruption** | Yes (resolve conflicts) | No |
**When to use Obsidian Git**:
- Need full version control (branches, tags, etc.)
- Already familiar with Git workflows
- Want integration with existing Git repos
- Don't mind manual conflict resolution
**When to use VaultLink**:
- Want automatic conflict-free merging
- Need real-time collaborative editing
- Don't want workflow interruptions from merge conflicts
- Prefer simpler mental model (sync, not commits)
**Key difference**: Git requires manual conflict resolution with `<<<<<<<` markers. VaultLink automatically merges all changes using operational transformation, never interrupting your workflow.
---
### Syncthing Integration
**Downloads**: ~22,600
**Repository**: https://github.com/LBF38/obsidian-syncthing-integration
**Overview**: Wrapper around Syncthing for file synchronization.
| Aspect | Syncthing Integration | VaultLink |
| ------------------------- | ------------------------------ | ----------------- |
| **Self-hosted** | Yes (Syncthing) | Yes (sync server) |
| **Conflict resolution** | Manual | Automatic |
| **Collaborative editing** | No | Yes |
| **Editor support** | Any | Any |
| **Status** | Unfinished | Production-ready |
| **Conflict files** | Creates `.sync-conflict` files | No conflict files |
| **Real-time sync** | Yes | Yes |
| **Automatic merging** | No | Yes |
**When to use Syncthing Integration**:
- Already use Syncthing for other files
- Don't need automatic conflict resolution
- Single-user with multiple devices
**When to use VaultLink**:
- Want automatic conflict resolution
- Need collaborative editing
- Want production-ready solution
- Don't want to manage conflict files
**Status note**: Syncthing Integration is marked as unfinished. VaultLink is production-ready with comprehensive testing.
---
### Remotely Sync
**Downloads**: ~38,000
**Repository**: https://github.com/sboesen/remotely-sync
**Overview**: Similar to Remotely Save, syncs to cloud storage.
| Aspect | Remotely Sync | VaultLink |
| ----------------------- | ----------------------- | ------------------- |
| **Self-hosted** | Partial (cloud storage) | Fully self-hosted |
| **Conflict resolution** | Limited/Paid | Free and automatic |
| **Code quality** | No tests | Comprehensive tests |
| **Maintenance** | Low activity | Active development |
**Same concerns as Remotely Save**: No test suite, conflict resolution limitations, cloud storage dependency.
**When to use VaultLink**: See Remotely Save comparison above.
---
### SyncFTP
**Downloads**: ~5,000
**Repository**: https://github.com/alex-donnan/SyncFTP
**Overview**: Simple FTP-based file synchronization.
| Aspect | SyncFTP | VaultLink |
| ------------------------- | ---------------------- | ---------------- |
| **Conflict resolution** | None (last write wins) | Automatic (OT) |
| **Data loss risk** | High (overwrites) | None (merges) |
| **Collaborative editing** | No | Yes |
| **Sophistication** | Minimal | Production-grade |
**When to use SyncFTP**: Don't use SyncFTP for any scenario where data integrity matters.
**When to use VaultLink**: Any scenario requiring reliable synchronization.
---
## Feature Comparison Matrix
| Feature | VaultLink | LiveSync | Relay | Git | Remotely Save | Syncthing |
| --------------------------------- | --------- | -------- | ----- | --- | ------------- | --------- |
| **Fully open source** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ |
| **Self-hosted** | ✅ | ✅ | ❌ | ✅ | Partial | ✅ |
| **Automatic conflict resolution** | ✅ | Basic | ✅ | ❌ | Paid | ❌ |
| **Real-time sync** | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ |
| **Collaborative editing** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ |
| **Cursor tracking** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **Editor agnostic** | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ |
| **Out-of-band edits** | ✅ | Limited | ❌ | ✅ | ❌ | ✅ |
| **No conflict markers** | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ |
| **Comprehensive tests** | ✅ | ❌ | ❌ | N/A | ❌ | N/A |
| **Simple deployment** | ✅ | ❌ | N/A | ❌ | ✅ | ❌ |
| **Low infrastructure** | ✅ | ❌ | N/A | ✅ | ✅ | ✅ |
---
## VaultLink's Unique Position
VaultLink is the **only** solution that combines:
1. **Fully open source** self-hosted server
2. **Editor agnostic** operation (not locked to Obsidian)
3. **Automatic conflict-free merging** using operational transformation
4. **Real-time collaborative editing** with cursor tracking
5. **Differential synchronization** supporting out-of-band edits
6. **Comprehensive test coverage** ensuring reliability
7. **Simple deployment** via Docker or single binary
## Use Case Recommendations
### Choose VaultLink when you:
- Edit vaults with multiple editors (Obsidian + Vim + VS Code)
- Need real-time collaboration with teammates
- Want automatic conflict resolution without manual intervention
- Value full control over infrastructure
- Need production-ready reliability with comprehensive testing
- Want to edit files while offline and sync later seamlessly
### Consider alternatives when you:
- **LiveSync**: Need end-to-end encryption and only use Obsidian
- **Git**: Need full version control with branches and advanced Git features
- **Remotely Save**: Already committed to cloud storage providers
- **Syncthing**: Already use Syncthing and don't need automatic merging
## Migration from Other Solutions
VaultLink works with plain Markdown files, making migration simple:
1. **From Git**: Clone your repo, point VaultLink to the directory
2. **From cloud sync**: Download files, configure VaultLink client
3. **From LiveSync**: Export vault, import to VaultLink
4. **From Syncthing**: Point VaultLink to synced directory
All solutions work with the same Markdown files—VaultLink just syncs them better.
## Beyond Obsidian
Because VaultLink is editor-agnostic, you can use it for:
- **Documentation teams**: Sync technical docs edited in VS Code
- **Academic writing**: Collaborate on papers with various Markdown editors
- **Personal knowledge bases**: Use Obsidian on mobile, Vim on servers
- **Automated workflows**: CLI client for backup systems and CI/CD
- **Multi-tool workflows**: Different team members use different editors
VaultLink doesn't lock you into Obsidian—it's a general-purpose differential sync system that happens to work excellently with Obsidian vaults.
## Next Steps
Ready to try VaultLink?
- [Get started →](/guide/getting-started)
- [Understand the architecture →](/architecture/)
- [See how sync works →](/architecture/sync-algorithm)

View file

@ -67,20 +67,20 @@ Create `docker-compose.yml`:
```yaml
services:
vaultlink-cli:
image: ghcr.io/schmelczer/vault-link-cli:latest
restart: unless-stopped
volumes:
- ./vault:/vault
command:
- "-l"
- "/vault"
- "-r"
- "wss://sync.example.com"
- "-t"
- "your-token"
- "-v"
- "default"
vaultlink-cli:
image: ghcr.io/schmelczer/vault-link-cli:latest
restart: unless-stopped
volumes:
- ./vault:/vault
command:
- "-l"
- "/vault"
- "-r"
- "wss://sync.example.com"
- "-t"
- "your-token"
- "-v"
- "default"
```
Start the client:
@ -93,22 +93,22 @@ docker compose up -d
### Required Arguments
| Argument | Short | Description | Example |
|----------|-------|-------------|---------|
| `--local-path` | `-l` | Local directory to sync | `/vault` |
| `--remote-uri` | `-r` | Server WebSocket URI | `wss://sync.example.com` |
| `--token` | `-t` | Authentication token | `abc123...` |
| `--vault-name` | `-v` | Vault name on server | `default` |
| Argument | Short | Description | Example |
| -------------- | ----- | ----------------------- | ------------------------ |
| `--local-path` | `-l` | Local directory to sync | `/vault` |
| `--remote-uri` | `-r` | Server WebSocket URI | `wss://sync.example.com` |
| `--token` | `-t` | Authentication token | `abc123...` |
| `--vault-name` | `-v` | Vault name on server | `default` |
### Optional Arguments
| Argument | Default | Description |
|----------|---------|-------------|
| `--sync-concurrency` | `1` | Concurrent file operations |
| `--max-file-size-mb` | `10` | Max file size in MB |
| `--ignore-pattern` | - | Glob pattern to ignore (repeatable) |
| `--websocket-retry-interval-ms` | `3500` | Reconnection interval |
| `--log-level` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
| Argument | Default | Description |
| ------------------------------- | ------- | -------------------------------------- |
| `--sync-concurrency` | `1` | Concurrent file operations |
| `--max-file-size-mb` | `10` | Max file size in MB |
| `--ignore-pattern` | - | Glob pattern to ignore (repeatable) |
| `--websocket-retry-interval-ms` | `3500` | Reconnection interval |
| `--log-level` | `INFO` | Log level: DEBUG, INFO, WARNING, ERROR |
### Environment Variables
@ -228,6 +228,7 @@ docker inspect --format='{{json .State.Health}}' vaultlink-sync | jq
```
Health check verifies:
- Health file exists
- Status updated within last 30 seconds
- WebSocket connection is active
@ -236,14 +237,14 @@ Configure custom health check:
```yaml
services:
vaultlink-cli:
image: ghcr.io/schmelczer/vault-link-cli:latest
healthcheck:
test: ["CMD", "node", "/app/healthcheck.js"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s
vaultlink-cli:
image: ghcr.io/schmelczer/vault-link-cli:latest
healthcheck:
test: ["CMD", "node", "/app/healthcheck.js"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s
```
### Read-Only Vault
@ -351,21 +352,25 @@ services:
### Client won't connect
**Check server accessibility**:
```bash
curl https://sync.example.com/vaults/test/ping
```
**Verify WebSocket protocol**:
- Use `ws://` for HTTP servers
- Use `wss://` for HTTPS servers
**Check authentication**:
- Token must match server config
- User must have access to the vault
### Permission errors
**Docker volume permissions**:
```bash
# Ensure directory is writable
chmod 755 /path/to/vault
@ -375,6 +380,7 @@ docker run --rm ghcr.io/schmelczer/vault-link-cli:latest id
```
**SELinux issues**:
```bash
# Add :z flag to volume mount
docker run -v /path/to/vault:/vault:z ...
@ -383,14 +389,17 @@ docker run -v /path/to/vault:/vault:z ...
### Files not syncing
**Check ignore patterns**:
- View logs to see which files are skipped
- Ensure patterns don't match unintentionally
**File size limits**:
- Check `--max-file-size-mb` setting
- Large files are skipped with a warning
**Check metadata**:
```bash
# View sync metadata
cat /path/to/vault/.vaultlink/metadata.json
@ -399,33 +408,39 @@ cat /path/to/vault/.vaultlink/metadata.json
### High memory usage
**Reduce concurrency**:
```bash
--sync-concurrency 1
```
**Limit file sizes**:
```bash
--max-file-size-mb 5
```
**Check vault size**:
- Very large vaults may need more resources
- Consider splitting into multiple vaults
### Connection keeps dropping
**Increase retry interval**:
```bash
--websocket-retry-interval-ms 5000
```
**Check network stability**:
```bash
# Monitor connection
docker logs -f vaultlink-sync | grep -i websocket
```
**Server timeout settings**:
- Verify reverse proxy WebSocket timeout
- Check server `response_timeout_seconds`
@ -503,6 +518,7 @@ WantedBy=multi-user.target
```
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable vaultlink-cli

View file

@ -74,9 +74,9 @@ You can connect to VaultLink using either the Obsidian plugin or the standalone
2. Browse community plugins and search for "VaultLink"
3. Install and enable the plugin
4. Configure the plugin:
- **Server URL**: `ws://localhost:3000` (or your server address)
- **Token**: The token from your `config.yml`
- **Vault Name**: `default` (or any name you choose)
- **Server URL**: `ws://localhost:3000` (or your server address)
- **Token**: The token from your `config.yml`
- **Vault Name**: `default` (or any name you choose)
[Read the full Obsidian plugin guide →](/guide/obsidian-plugin)
@ -119,20 +119,20 @@ To add more users or restrict vault access:
```yaml
users:
user_configs:
- name: alice
token: alice-secure-token
vault_access:
type: allow_list
allowed:
- personal
- shared
- name: bob
token: bob-secure-token
vault_access:
type: allow_list
allowed:
- shared
user_configs:
- name: alice
token: alice-secure-token
vault_access:
type: allow_list
allowed:
- personal
- shared
- name: bob
token: bob-secure-token
vault_access:
type: allow_list
allowed:
- shared
```
[Learn about authentication configuration →](/config/authentication)
@ -159,11 +159,13 @@ Want to understand how VaultLink works under the hood?
### Server won't start
Check Docker logs:
```bash
docker logs vaultlink-server
```
Common issues:
- Port 3000 already in use: Change the port mapping `-p 3001:3000`
- Config file errors: Validate YAML syntax
- Permission issues: Ensure the volume mount is writable

View file

@ -27,6 +27,7 @@ After installation, configure the plugin in **Settings → VaultLink**.
### Required Settings
#### Server URL
The WebSocket URL of your sync server.
- **Development/Local**: `ws://localhost:3000`
@ -37,14 +38,17 @@ Use `ws://` for unencrypted connections and `wss://` for SSL connections (produc
:::
#### Authentication Token
Your authentication token from the server's `config.yml`.
Generate a secure token:
```bash
openssl rand -hex 32
```
#### Vault Name
The name of the vault on the server. Can be any string.
Multiple Obsidian vaults can sync to the same server vault name (for shared vaults), or use unique names for separate vaults.
@ -52,26 +56,34 @@ Multiple Obsidian vaults can sync to the same server vault name (for shared vaul
### Optional Settings
#### Sync Concurrency
Number of files to sync simultaneously.
- **Default**: 1
- **Range**: 1-10
- Higher values = faster initial sync, more resource usage
#### Max File Size
Maximum file size to sync (in MB).
- **Default**: 10
- Files larger than this are skipped
#### Ignore Patterns
Glob patterns for files to exclude from sync.
Examples:
- `*.tmp` - Ignore temporary files
- `.trash/**` - Ignore trash folder
- `private/**` - Ignore private directory
#### WebSocket Retry Interval
Milliseconds between reconnection attempts when disconnected.
- **Default**: 3500ms
- Increase for flaky networks to avoid connection spam
@ -172,24 +184,26 @@ Share specific folders while keeping others private:
### Plugin won't connect
1. **Verify server is running**:
```bash
curl http://your-server:3000/vaults/test/ping
```
Should return `pong`
```bash
curl http://your-server:3000/vaults/test/ping
```
Should return `pong`
2. **Check URL format**:
- Local: `ws://localhost:3000`
- Remote (SSL): `wss://sync.example.com`
- Don't include `/vault/name` in the URL
- Local: `ws://localhost:3000`
- Remote (SSL): `wss://sync.example.com`
- Don't include `/vault/name` in the URL
3. **Verify token**:
- Must match server config exactly
- No extra spaces or quotes
- Check server logs for authentication errors
- Must match server config exactly
- No extra spaces or quotes
- Check server logs for authentication errors
4. **Check firewall**:
- Ensure port is accessible from your network
- For mobile, server must be publicly accessible or use VPN
- Ensure port is accessible from your network
- For mobile, server must be publicly accessible or use VPN
### Files not syncing

View file

@ -35,21 +35,21 @@ Create `docker-compose.yml`:
```yaml
services:
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
container_name: vaultlink-server
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./data:/data
command: ["/app/sync_server", "/data/config.yml"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/vaults/fake/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
vaultlink-server:
image: ghcr.io/schmelczer/vault-link-server:latest
container_name: vaultlink-server
restart: unless-stopped
ports:
- "3000:3000"
volumes:
- ./data:/data
command: ["/app/sync_server", "/data/config.yml"]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/vaults/fake/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
```
Start the server:
@ -76,6 +76,7 @@ chmod +x sync_server-linux-x86_64
### Build from Source
Requirements:
- Rust 1.89.0 or later
- SQLite development headers
- SQLx CLI
@ -106,27 +107,27 @@ Create a `config.yml` file with your server configuration:
```yaml
database:
databases_directory_path: databases
max_connections_per_vault: 12
cursor_timeout_seconds: 60
databases_directory_path: databases
max_connections_per_vault: 12
cursor_timeout_seconds: 60
server:
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 60
host: 0.0.0.0
port: 3000
max_body_size_mb: 512
max_clients_per_vault: 256
response_timeout_seconds: 60
users:
user_configs:
- name: admin
token: your-secure-random-token-here
vault_access:
type: allow_access_to_all
user_configs:
- name: admin
token: your-secure-random-token-here
vault_access:
type: allow_access_to_all
logging:
log_directory: logs
log_rotation: 7days
log_directory: logs
log_rotation: 7days
```
### Configuration Fields
@ -192,6 +193,7 @@ server {
```
Reload Nginx:
```bash
sudo nginx -t
sudo systemctl reload nginx
@ -208,6 +210,7 @@ sync.example.com {
```
Start Caddy:
```bash
caddy run --config Caddyfile
```
@ -269,6 +272,7 @@ find /backup/vaultlink -type d -mtime +30 -exec rm -rf {} +
```
Run daily via cron:
```cron
0 2 * * * /opt/vaultlink/backup.sh
```
@ -293,12 +297,14 @@ For advanced monitoring, collect Docker stats or implement custom metrics.
#### Log Monitoring
Logs are written to the configured `log_directory`. Monitor for:
- Connection failures
- Authentication errors
- Database errors
- WebSocket disconnections
Example log watching:
```bash
tail -f /data/logs/*.log | grep -i error
```
@ -316,11 +322,13 @@ VaultLink currently uses SQLite, which limits horizontal scaling. For multiple s
### Vertical Scaling
Increase resources for the server:
- More CPU for handling concurrent connections
- More RAM for database caching
- Faster storage (SSD) for database operations
Tune configuration:
- Increase `max_clients_per_vault` for more concurrent users
- Increase `max_connections_per_vault` for database performance
- Adjust `max_body_size_mb` based on typical file sizes

View file

@ -9,6 +9,7 @@ VaultLink consists of three main components:
### Sync Server
A Rust-based WebSocket server that handles:
- Real-time bidirectional synchronization
- Document versioning with SQLite
- User authentication and vault access control
@ -17,6 +18,7 @@ A Rust-based WebSocket server that handles:
### Obsidian Plugin
A native Obsidian plugin that:
- Integrates sync directly into your Obsidian workflow
- Provides real-time updates as you edit
- Handles file watching and automatic synchronization
@ -25,6 +27,7 @@ A native Obsidian plugin that:
### CLI Client
A standalone synchronization client that:
- Syncs vaults without requiring Obsidian
- Perfect for servers, automation, or backup systems
- Provides file watching and bidirectional sync
@ -39,6 +42,7 @@ Changes are synchronized immediately via WebSocket connections. When multiple us
### Self-Hosted Architecture
Run the sync server on your own infrastructure:
- Full control over data storage and access
- No dependency on third-party services
- Configurable authentication and authorization
@ -47,6 +51,7 @@ Run the sync server on your own infrastructure:
### Operational Transformation
VaultLink uses the `reconcile-text` library for intelligent conflict resolution:
- Simultaneous edits are automatically merged
- No manual conflict resolution required
- Preserves intent of all contributors
@ -55,6 +60,7 @@ VaultLink uses the `reconcile-text` library for intelligent conflict resolution:
### Flexible Authentication
Configure user access per vault:
- Token-based authentication
- Per-user vault access control
- Allow-list or deny-list patterns
@ -65,6 +71,7 @@ Configure user access per vault:
### Personal Sync
Synchronize your Obsidian vault across multiple devices:
- Laptop, desktop, and mobile in real-time
- No cloud service subscription required
- Full privacy and data control
@ -72,6 +79,7 @@ Synchronize your Obsidian vault across multiple devices:
### Team Collaboration
Share knowledge bases with teammates:
- Real-time collaborative editing
- Granular access control per vault
- Self-hosted for enterprise security requirements
@ -79,6 +87,7 @@ Share knowledge bases with teammates:
### Automated Backups
Use the CLI client for automated workflows:
- Scheduled backups to remote servers
- Integration with existing backup systems
- Headless operation without Obsidian
@ -86,6 +95,7 @@ Use the CLI client for automated workflows:
### Development & Testing
Synchronize documentation across environments:
- Keep docs in sync with development environments
- Automated deployment of documentation
- Version control integration

View file

@ -2,39 +2,39 @@
layout: home
hero:
name: VaultLink
text: Self-Hosted Sync for Obsidian
tagline: Real-time collaborative file synchronization for your knowledge base
image:
src: /logo.svg
alt: VaultLink
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
- theme: alt
text: View on GitHub
link: https://github.com/schmelczer/vault-link
name: VaultLink
text: Self-Hosted Sync for Obsidian
tagline: Real-time collaborative file synchronization for your knowledge base
image:
src: /logo.svg
alt: VaultLink
actions:
- theme: brand
text: Get Started
link: /guide/getting-started
- theme: alt
text: View on GitHub
link: https://github.com/schmelczer/vault-link
features:
- icon: 🚀
title: Real-Time Synchronization
details: Operational transformation-based conflict resolution ensures your files stay in sync across devices without data loss
- icon: 🔒
title: Self-Hosted & Private
details: Run your own sync server. Your data stays on your infrastructure with full control over access and privacy
- icon: 🎯
title: Obsidian Plugin
details: Native integration with Obsidian for seamless synchronization directly within your favorite note-taking app
- icon: 🖥️
title: CLI Client
details: Sync vaults to any system using the standalone CLI client. Perfect for servers, automation, or headless setups
- icon: ⚡
title: Built for Performance
details: Rust-powered WebSocket server with SQLite backend delivers blazing-fast sync performance
- icon: 🛠️
title: Flexible Deployment
details: Deploy via Docker, binary releases, or build from source. Configure authentication and access controls to fit your needs
- icon: 🚀
title: Real-Time Synchronization
details: Operational transformation-based conflict resolution ensures your files stay in sync across devices without data loss
- icon: 🔒
title: Self-Hosted & Private
details: Run your own sync server. Your data stays on your infrastructure with full control over access and privacy
- icon: 🎯
title: Obsidian Plugin
details: Native integration with Obsidian for seamless synchronization directly within your favorite note-taking app
- icon: 🖥️
title: CLI Client
details: Sync vaults to any system using the standalone CLI client. Perfect for servers, automation, or headless setups
- icon: ⚡
title: Built for Performance
details: Rust-powered WebSocket server with SQLite backend delivers blazing-fast sync performance
- icon: 🛠️
title: Flexible Deployment
details: Deploy via Docker, binary releases, or build from source. Configure authentication and access controls to fit your needs
---
## Quick Start

View file

@ -6,12 +6,15 @@
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview"
"preview": "vitepress preview",
"format": "prettier --write \"**/*.md\" \"**/*.mts\"",
"format:check": "prettier --check \"**/*.md\" \"**/*.mts\""
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"prettier": "^3.6.2",
"vitepress": "^1.6.4",
"vue": "^3.5.24"
}

View file

@ -1,34 +1,47 @@
<svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:1" />
<stop offset="100%" style="stop-color:#357ABD;stop-opacity:1" />
</linearGradient>
</defs>
<!-- Background circle -->
<circle cx="100" cy="100" r="95" fill="#4A90E2" opacity="0.1"/>
<circle cx="100" cy="100" r="90" fill="url(#grad1)" opacity="0.15"/>
<!-- Link chain symbol -->
<!-- Main vault icon -->
<g transform="translate(100, 100)">
<!-- Left link -->
<path d="M -60 -10 L -30 -10 C -20 -10 -15 -5 -15 5 L -15 5 C -15 15 -20 20 -30 20 L -60 20 C -70 20 -75 15 -75 5 L -75 -5 C -75 -15 -70 -20 -60 -20 Z"
fill="none" stroke="#4A90E2" stroke-width="8" stroke-linecap="round"/>
<!-- Vault body -->
<rect x="-45" y="-50" width="90" height="80" rx="8" fill="none" stroke="url(#grad1)" stroke-width="6"/>
<!-- Right link -->
<path d="M 60 -10 L 30 -10 C 20 -10 15 -5 15 5 L 15 5 C 15 15 20 20 30 20 L 60 20 C 70 20 75 15 75 5 L 75 -5 C 75 -15 70 -20 60 -20 Z"
fill="none" stroke="#4A90E2" stroke-width="8" stroke-linecap="round"/>
<!-- Vault door circle -->
<circle cx="0" cy="-10" r="22" fill="none" stroke="url(#grad1)" stroke-width="5"/>
<circle cx="0" cy="-10" r="14" fill="none" stroke="url(#grad1)" stroke-width="3"/>
<circle cx="0" cy="-10" r="6" fill="url(#grad1)"/>
<!-- Center connecting bar -->
<rect x="-15" y="-6" width="30" height="12" rx="6" fill="#4A90E2"/>
<!-- Vault handle -->
<line x1="0" y1="-4" x2="18" y2="-4" stroke="url(#grad1)" stroke-width="3" stroke-linecap="round"/>
<circle cx="18" cy="-4" r="4" fill="url(#grad1)"/>
<!-- Vault door detail -->
<circle cx="0" cy="0" r="12" fill="none" stroke="#4A90E2" stroke-width="3"/>
<circle cx="0" cy="0" r="6" fill="#4A90E2"/>
<!-- Link chain -->
<g opacity="0.9">
<!-- Left link -->
<ellipse cx="-30" cy="40" rx="12" ry="8" fill="none" stroke="url(#grad1)" stroke-width="4"/>
<!-- Right link -->
<ellipse cx="30" cy="40" rx="12" ry="8" fill="none" stroke="url(#grad1)" stroke-width="4"/>
<!-- Center link connecting them -->
<ellipse cx="0" cy="40" rx="12" ry="8" fill="none" stroke="url(#grad1)" stroke-width="4"/>
</g>
<!-- Sync arrows -->
<g opacity="0.6">
<!-- Top arrow -->
<path d="M -5 -50 L 5 -50 L 0 -40 Z" fill="#4A90E2"/>
<!-- Bottom arrow -->
<path d="M 5 50 L -5 50 L 0 40 Z" fill="#4A90E2"/>
<!-- Sync arrows (subtle) -->
<g opacity="0.5">
<!-- Clockwise arrow top-right -->
<path d="M 35 -35 Q 50 -35 50 -20 L 50 -15" fill="none" stroke="url(#grad1)" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="50,-15 47,-22 53,-22" fill="url(#grad1)"/>
<!-- Counter-clockwise arrow bottom-left -->
<path d="M -35 25 Q -50 25 -50 10 L -50 5" fill="none" stroke="url(#grad1)" stroke-width="2.5" stroke-linecap="round"/>
<polygon points="-50,5 -47,12 -53,12" fill="url(#grad1)"/>
</g>
</g>
<!-- Text (optional) -->
<text x="100" y="175" font-family="Arial, sans-serif" font-size="24" font-weight="bold"
text-anchor="middle" fill="#4A90E2">VaultLink</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After