34 KiB
Self-hosted LiveSync CLI
Command-line version of Self-hosted LiveSync plug-in for syncing vaults without Obsidian.
Features
- ✅ Sync Obsidian vaults using CouchDB without running Obsidian
- ✅ Compatible with Self-hosted LiveSync plug-in settings
- ✅ Supports all core sync features (encryption, conflict resolution, etc.)
- ✅ Lightweight and headless operation
- ✅ Cross-platform (Windows, macOS, Linux)
Architecture
This CLI version is built using the same core as the Obsidian plug-in:
CLI Main
└─ NodeServiceContext (events, translation, database root, and injected standard I/O)
└─ LiveSyncBaseCore<NodeServiceContext, IMinimumLiveSyncCommands>
├─ NodeServiceHub (All services without Obsidian dependencies)
└─ ServiceModules (wired by initialiseServiceModulesCLI)
├─ FileAccessCLI (Node.js FileSystemAdapter)
├─ StorageEventManagerCLI
├─ ServiceFileAccessCLI
├─ ServiceDatabaseFileAccessCLI
├─ ServiceFileHandler
└─ ServiceRebuilder
Key Components
-
Node.js FileSystem Adapter (
adapters/)- Platform-agnostic file operations using Node.js
fs/promises - Implements same interface as Obsidian's file system
- Platform-agnostic file operations using Node.js
-
Service Modules (
serviceModules/)- Initialised by
initialiseServiceModulesCLI - All core sync functionality preserved
- Initialised by
-
Service Hub and Settings Services (
services/)NodeServiceContextowns the host-selected database root and standard input/output implementationNodeServiceHubreceives that exact Context instead of constructing platform capabilities implicitly- Internal adapter diagnostics use injected callbacks wired to the service logging API
- Node-specific settings and key-value services are provided without Obsidian dependencies
-
Main Entry Point (
main.ts)- Command-line interface
- Settings management (JSON file)
- Graceful shutdown handling
Command overview
The CLI operates on a database directory which contains PouchDB data and settings.
Note
livesync-cliis the alias for the CLI executable. Please replace with the actual command of your installation (e.g.npm run --silent cli --ordocker run ...).
livesync-cli [database-path] [command] [args...]
Arguments
database-path: Path to the directory where.livesyncfolder andsettings.jsonare (or will be) located.- Note: In previous versions, this was referred to as the "vault" path. Now it is clearly distinguished from the actual vault (the directory containing your
.mdfiles).
- Note: In previous versions, this was referred to as the "vault" path. Now it is clearly distinguished from the actual vault (the directory containing your
--vault <path>/-V <path>: (daemon/mirror/rebuild/fetch) Path to the Vault directory containing.mdfiles.- Allows the PouchDB database directory and the actual vault directory to be different locations.
- For
mirrorcommand, the positional[vault-path]argument takes precedence over--vault.
--force/-f: Required byrebuildandfetchbecause they replace a database. It also retains its existinginit-settingsoverwrite behaviour.--write-settings: Write setting migrations and other lasting changes after the command succeeds.init-settingswrites its target file.setup,remote-add,remote-rm,remote-set, andremote-activatewrite their settings changes without this option.- All remaining commands leave the settings file unchanged by default.
- Temporary values used to suspend synchronisation or select a remote for one command are never written.
--compat-remote-admin-exit-zero: Preserve the former zero exit code whenmark-resolved,lock-remote, orunlock-remotereturns a provider verification failure.- Without this option, those commands return a non-zero exit code when verification fails.
- Invalid arguments, unknown remote IDs, and errors thrown while activating or mutating the remote remain errors with or without this option.
Commands
sync: Run one replication cycle with the remote CouchDB.mirror [vault-path]: Bidirectional sync between the local database and a local directory (the actual vault).- If
vault-pathis provided, the CLI will synchronise the database with files in the vault directory. - If
vault-pathis omitted, it defaults todatabase-path(compatibility mode). - Use this command to keep your local
.mdfiles in sync with the database.
- If
rebuild: Rebuild the local and remote databases from the selected Vault. This replaces existing local and remote database state and requires--force.- This command may select a new local database suffix. Add
--write-settingsif later CLI commands should use the resulting database selection; otherwise the existing settings file remains unchanged.
- This command may select a new local database suffix. Add
fetch: Replace the local database with the configured remote state, then reflect fetched files to the selected Vault. Existing local database state is discarded, so this command requires--force.ls [prefix]: List files currently stored in the local database.push <src> <dst>: Push a local file<src>into the database at path<dst>.pull <src> <dst>: Pull a file<src>from the database into local file<dst>.cat <src>: Read a file from the database and write to stdout.put <dst>: Read from stdin and write to the database path<dst>.remote-add <name> <connstr>: Add a remote configuration from a connection string.remote-rm <remote-id>: Remove a remote configuration by ID.remote-ls: List remote configurations (id,name,active/inactive, redacted URI).remote-export <remote-id>: Export the stored connection string by remote ID.remote-set <remote-id> <connstr>: Replace the stored connection string by remote ID.remote-activate <remote-id>: Activate a remote configuration by ID.mark-resolved [remote-id]: Resolve remote synchronisation status.unlock-remote [remote-id]: Unlock the remote database.lock-remote [remote-id]: Lock the remote database.remote-status [remote-id]: Show remote database status.init-settings [file]: Create a default settings file.
Remote-administration commands verify the resulting milestone state through the selected provider. The existing [Verification] lines remain suitable for scripts which inspect command output, while the default exit code now reflects whether that verification succeeded.
Examples
# Basic sync with remote
livesync-cli ./my-db sync
# Mirroring to your actual Obsidian vault
livesync-cli ./my-db mirror /path/to/obsidian-vault
# Rebuild local and remote databases from an authoritative Vault
livesync-cli ./my-db --vault /path/to/obsidian-vault rebuild --force --write-settings
# Replace the local database and Vault files from the configured remote
livesync-cli ./my-db --vault /path/to/obsidian-vault fetch --force --write-settings
# Manual file operations
livesync-cli ./my-db push ./note.md folder/note.md
livesync-cli ./my-db pull folder/note.md ./note.md
Installation
Build from source
# Clone the repository
git clone <repository-url>
cd obsidian-livesync
# Install dependencies from the repository root
npm install
# Build the CLI from the repository root
npm run build -w self-hosted-livesync-cli
# Or from the package directory
cd src/apps/cli
npm run build
The shared core is installed as the exact @vrtmrz/livesync-commonlib package artefact recorded in the root lockfile.
Run the CLI:
# Run with npm workspace script (from repository root)
npm run cli -w self-hosted-livesync-cli -- [database-path] [command] [args...]
# Or from the package directory
cd src/apps/cli
npm run cli -- [database-path] [command] [args...]
# Run the built executable directly
node src/apps/cli/dist/index.cjs [database-path] [command] [args...]
systemd installation
The deploy/ directory contains a systemd unit template and an install script.
Automated installation (user service, recommended):
bash src/apps/cli/deploy/install.sh --vault /path/to/vault
With a polling interval:
bash src/apps/cli/deploy/install.sh --vault /path/to/vault --interval 60
System-wide installation (requires root or sudo for /etc/systemd/system/):
bash src/apps/cli/deploy/install.sh --system --vault /path/to/vault
The script:
- Installs the repository dependencies and builds the CLI.
- Installs the complete CLI bundle and its production dependencies under
~/.local/lib/livesync-cli(user) or/usr/local/lib/livesync-cli(system), then checks that the installed CLI can start. - Installs the command wrapper as
~/.local/bin/livesync-cli(user) or/usr/local/bin/livesync-cli(system). - Writes the unit file to
~/.config/systemd/user/livesync-cli.service(user) or/etc/systemd/system/livesync-cli.service(system). - Reloads systemd, enables and starts the service, and reports success only after confirming that the service remains active.
Ensure that ~/.local/bin for a user installation, or /usr/local/bin for a system-wide installation, is on the shell's PATH before invoking livesync-cli interactively. For example, add the following to the appropriate shell start-up file for a user installation when needed:
export PATH="$HOME/.local/bin:$PATH"
The generated systemd unit uses the wrapper's absolute path and does not depend on the shell's PATH.
Manual setup — if you prefer to manage the unit yourself, copy deploy/livesync-cli.service, replace LIVESYNC_BIN and LIVESYNC_VAULT_PATH with the actual binary path and Vault path, then install it in the appropriate systemd directory.
Docker
CLI-only Docker patches use their full release tag, such as ghcr.io/vrtmrz/livesync-cli:1.0.36-patch1-cli. Patch and RC images receive version and SHA-qualified tags. The latest and major-minor tags are updated by ordinary stable releases. CLI image versions come from the release Git tag, so a CLI-only patch does not require changing the Obsidian plug-in version.
A Docker image is provided for headless / server deployments. Build from the repository root:
docker build -f src/apps/cli/Dockerfile -t livesync-cli .
Run:
# Sync with CouchDB
docker run --rm -v /path/to/your/db:/data livesync-cli sync
# Mirror to a specific vault directory
docker run --rm -v /path/to/your/db:/data -v /path/to/your/vault:/vault livesync-cli mirror /vault
# List files in the local database
docker run --rm -v /path/to/your/db:/data livesync-cli ls
The database directory is mounted at /data by default. Override with -e LIVESYNC_DB_PATH=/other/path.
P2P (WebRTC) and Docker networking
The P2P replicator (p2p-host, p2p-sync, p2p-peers) uses WebRTC and generates
three kinds of ICE candidates. The default Docker bridge network affects which
candidates are usable:
| Candidate type | Description | Bridge network |
|---|---|---|
host |
Container bridge IP (172.17.x.x) |
Unreachable from LAN peers |
srflx |
Host public IP via STUN reflection | Works over the internet |
relay |
Traffic relayed via TURN server | Always reachable |
LAN P2P on Linux — use --network host so that the real host IP is
advertised as the host candidate:
docker run --rm --network host -v /path/to/your/vault:/data livesync-cli p2p-host
Note: also fix the alias to include --network host if you want to use livesync-cli for P2P commands.
--network hostis not available on Docker Desktop for macOS or Windows.
LAN P2P on macOS / Windows Docker Desktop — configure a TURN server in the
settings file (P2P_turnServers, P2P_turnUsername, P2P_turnCredential).
All P2P traffic will then be relayed through the TURN server, bypassing the
bridge-network limitation.
Internet P2P — the default bridge network is sufficient. The srflx
candidate carries the host's public IP and peers can connect normally.
CouchDB sync only (no P2P) — no special network configuration is required.
Adding livesync-cli alias
If you used the systemd installer, no alias is required: it installs the livesync-cli wrapper in ~/.local/bin or /usr/local/bin. If the installed command is not found, follow the PATH guidance in the systemd installation section.
The aliases below are only for running the CLI from a source checkout, or from Docker without using the installer. Add the appropriate alias to your shell configuration file, such as .zshrc or .bashrc.
If you are using npm run, add the following line:
alias livesync-cli='npm run --silent --prefix /path/to/repository/src/apps/cli cli --'
# or
alias livesync-cli="npm run --silent --prefix $PWD cli --"
Alternatively, if you want to use the built executable directly:
alias livesync-cli='node /path/to/repository/src/apps/cli/dist/index.cjs'
or
alias livesync-cli="node $PWD/dist/index.cjs"
If you prefer using Docker:
alias livesync-cli='docker run --rm -v /path/to/your/db:/data livesync-cli'
After adding the alias, restart your shell or run source ~/.zshrc (or .bashrc).
Usage
Basic Usage
As you know, the CLI is designed to be used in a headless environment. Hence all operations are performed against a local vault directory and a settings file. Here are some example commands:
# Sync local database with CouchDB (no files will be changed).
livesync-cli /path/to/your-local-database --settings /path/to/settings.json sync
# Push files to local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json push /your/storage/file.md /vault/path/file.md
# Pull files from local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull /vault/path/file.md /your/storage/file.md
# Verbose logging
livesync-cli /path/to/your-local-database --settings /path/to/settings.json --verbose
# Apply setup URI to settings file (settings only; does not run synchronisation)
livesync-cli /path/to/your-local-database --settings /path/to/settings.json setup "obsidian://setuplivesync?settings=..."
# Put text from stdin into local database
echo "Hello from stdin" | livesync-cli /path/to/your-local-database --settings /path/to/settings.json put /vault/path/file.md
# Output a file from local database to stdout
livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat /vault/path/file.md
# Output a specific revision of a file from local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json cat-rev /vault/path/file.md 3-abcdef
# Pull a specific revision of a file from local database to local storage
livesync-cli /path/to/your-local-database --settings /path/to/settings.json pull-rev /vault/path/file.md /your/storage/file.old.md 3-abcdef
# List files in local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json ls /vault/path/
# Show metadata for a file in local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json info /vault/path/file.md
# Mark a file as deleted in local database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json rm /vault/path/file.md
# Resolve conflict by keeping a specific revision
livesync-cli /path/to/your-local-database --settings /path/to/settings.json resolve /vault/path/file.md 3-abcdef
# Add, list, activate, and remove remote configurations
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-add main "sls+https://user:pass@example.com/db"
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-ls
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-export remote-abc123
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-set remote-abc123 "sls+p2p://room-abc?passphrase=secret"
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-activate remote-abc123
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-rm remote-abc123
# Lock, unlock, resolve, and view status of remote database
livesync-cli /path/to/your-local-database --settings /path/to/settings.json remote-status remote-abc123
livesync-cli /path/to/your-local-database --settings /path/to/settings.json lock-remote remote-abc123
livesync-cli /path/to/your-local-database --settings /path/to/settings.json mark-resolved remote-abc123
livesync-cli /path/to/your-local-database --settings /path/to/settings.json unlock-remote remote-abc123
Configuration
The CLI uses the same settings format as the Obsidian plug-in. Create a .livesync/settings.json file in your vault directory:
{
"couchDB_URI": "http://localhost:5984",
"couchDB_USER": "admin",
"couchDB_PASSWORD": "password",
"couchDB_DBNAME": "obsidian-livesync",
"liveSync": true,
"syncOnSave": true,
"syncOnStart": true,
"encrypt": true,
"passphrase": "your-encryption-passphrase",
"usePluginSync": false,
"isConfigured": true
}
Minimum required settings:
couchDB_URI: CouchDB server URLcouchDB_USER: CouchDB usernamecouchDB_PASSWORD: CouchDB passwordcouchDB_DBNAME: Database nameisConfigured: Set totrueafter configuration
Unicode path identity
The pathNormalizationVersion setting controls how file paths identify
Metadata documents when using CouchDB. An absent value or 0 keeps the legacy
identity. Version 1 uses Unicode Normalization Form C (NFC) for Metadata IDs
and path comparison keys. It does not rewrite Metadata.path, rename existing
files, or change Chunks or file content. handleFilenameCaseSensitive remains
an independent case-handling setting; the CLI resolves NFC-equivalent names
even when case-sensitive handling is enabled.
When the CLI resolves an existing path, it uses the actual spelling of each
matching file or directory for reads, writes, deletes, stat calls, directory
creation, and renames. If a path component is missing, it uses the actual
existing parent and preserves the spelling received for the new component. If
a file or any parent directory has multiple candidates for the same logical
key after NFC and the existing case rule, the CLI warns and refuses the
operation. It does not choose a candidate based on directory order, merge
entries, or rename them.
Version 1 requires a coordinated Rebuild and Fetch; changing the setting by
itself does not migrate a database. Update every participating client, stop
synchronisation, and back up a complete authoritative Vault. Set
pathNormalizationVersion to 1 on the source client, then run rebuild
with --force --write-settings. Set the same value on each remaining CLI
client, then run fetch with --force --write-settings. Use Overwrite
Server Data with This Device's Files and Reset Synchronisation on This
Device for the corresponding Obsidian Rebuild and Fetch operations. Resolve
any ambiguous names before reconstruction. Version 1 is supported with
CouchDB only; do not resume a legacy client against the rebuilt database.
Command-line Reference
Usage:
livesync-cli <database-path> [options] <command> [command-args]
livesync-cli init-settings [path]
Arguments:
database-path Path to the local database directory (required except for init-settings)
Options:
--settings, -s <path> Path to settings file (default: .livesync/settings.json in local database directory)
--force, -f Required by rebuild/fetch; overwrite existing file on init-settings
--verbose, -v Enable verbose logging
--debug, -d Enable debug logging (includes verbose)
--interval <N>, -i <N> (daemon only) Poll CouchDB every N seconds instead of using the _changes feed
--vault <path>, -V <path> (daemon/mirror/rebuild/fetch) Path to vault directory, decoupled from database-path
--write-settings Write setting changes after a successful command
--compat-remote-admin-exit-zero
Preserve the former zero exit code when remote-administration verification fails
--help, -h Show this help message
Commands:
daemon (default) Run mirror scan then continuously sync CouchDB <-> local filesystem
init-settings [path] Create unconfigured settings JSON with the new-Vault recommendations
sync Run one replication cycle and exit
p2p-peers <timeout> Show discovered peers as [peer]<TAB><peer-id><TAB><peer-name>
p2p-sync <peer> <timeout> Synchronise with specified peer-id or peer-name
p2p-host Start P2P host mode and wait until interrupted (Ctrl+C)
rebuild Rebuild local and remote databases from the selected Vault (requires --force)
fetch Replace the local database with remote state and reflect it to the Vault (requires --force)
push <src> <dst> Push local file <src> into local database path <dst>
pull <src> <dst> Pull file <src> from local database into local file <dst>
pull-rev <src> <dst> <rev> Pull specific revision <rev> into local file <dst>
setup <setupURI> Apply setup URI to settings file
put <dst> Read text from standard input and write to local database path <dst>
cat <src> Write latest file content from local database to standard output
cat-rev <src> <rev> Write specific revision <rev> content from local database to standard output
ls [prefix] List files as path<TAB>size<TAB>mtime<TAB>revision[*]
info <path> Show file metadata including current and past revisions, conflicts, and chunk list
rm <path> Mark file as deleted in local database
resolve <path> <rev> Resolve conflict by keeping the specified revision
mirror [vaultPath] Mirror database contents to the local file system
(vaultPath positional arg > --vault flag > database-path)
Run via npm script:
npm run --silent cli -- [database-path] [options] [command] [command-args]
Detailed Command Descriptions
ls
ls lists files in the local database with optional prefix filtering. Output format is:
Note: * indicates if the file has conflicts.
p2p-peers
p2p-peers <timeout> waits for the specified number of seconds, then prints each discovered peer on a separate line:
[peer]<TAB><peer-id><TAB><peer-name>
Use this command to select a target for p2p-sync.
p2p-sync
p2p-sync <peer> <timeout> discovers peers up to the specified timeout and synchronises with the selected peer.
<peer>accepts eitherpeer-idorpeer-namefromp2p-peersoutput.- On success, the command prints a completion message to standard error and exits with status code
0. - On failure, the command prints an error message and exits non-zero.
p2p-host
p2p-host starts the local P2P host and keeps running until interrupted.
- Other peers can discover and synchronise with this host while it is running.
- Stop the host with
Ctrl+C. - In CLI mode, behaviour is non-interactive and acceptance follows settings.
After the local database is ready, the host restores its configured P2P settings
before connecting. It opens the connection explicitly, including when
P2P_AutoStart is false.
P2P_AutoSyncPeersselects peers for an initial bidirectional synchronisation.P2P_AutoWatchPeersselects peers whose later announced changes the host fetches. Those peers must enableP2P_AutoBroadcast.P2P_AutoBroadcastenables announcements of changes in the host's local database. Clients must follow the host to fetch those changes automatically.- Headless operation requires accepted peers. Configure
P2P_AutoAcceptingPeersor retain an existing acceptance decision; selecting a peer for automatic synchronisation does not itself accept that peer.
For example, a P2P-only host for peers named Desktop and android can use:
{
"remoteType": "ONLY_P2P",
"P2P_Enabled": true,
"P2P_IsHeadless": true,
"P2P_AutoStart": false,
"P2P_AutoBroadcast": true,
"P2P_AutoAcceptingPeers": "Desktop,android",
"P2P_AutoSyncPeers": "Desktop,android",
"P2P_AutoWatchPeers": "Desktop,android"
}
Add these fields to settings which already contain the shared P2P room, passphrase, signalling relay, and this host's unique peer name. Configure the clients to accept, synchronise with, and follow the host, and to announce their own changes. With a persistent local database, the host can retain received changes for a client which connects later.
p2p-host exchanges database content. It does not scan or watch external Vault
file edits. Single-operation CLI commands keep automatic synchronisation
suspended during their execution.
info
info output fields:
id: Document IDrevision: Current revisionconflicts: Conflicted revisions, orN/Afilename: Basename of pathpath: Vault-relative pathsize: Size in bytesrevisions: Available non-current revisionschunks: Number of chunk IDschildren: Chunk ID list
mirror
mirror is a command that synchronises your storage with your local vault. It is essentially a process that runs upon startup in Obsidian.
In other words, it performs the following actions:
-
Precondition checks — Aborts early if any of the following conditions are not met:
- Settings must be configured (
isConfigured: true). - File watching must not be suspended (
suspendFileWatching: false). - Remediation mode must be inactive (
maxMTimeForReflectEvents: 0).
- Settings must be configured (
-
State restoration — On subsequent runs (after the first successful scan), restores the previous storage state before proceeding.
-
Expired deletion cleanup — If
automaticallyDeleteMetadataOfDeletedFilesis set to a positive number of days, any document that is marked deleted and whosemtimeis older than the retention period is permanently removed from the local database. -
File collection — Enumerates files from two sources:
- Storage: all files under the vault path that pass
isTargetFile. - Local database: all normal documents (fetched with conflict information) whose paths are valid and pass
isTargetFile. - Both collections build case-insensitive ↔ case-sensitive path maps, controlled by
handleFilenameCaseSensitive.
- Storage: all files under the vault path that pass
-
Categorisation and synchronisation — The union of both file sets is split into three groups and processed concurrently (up to 10 files at a time):
Group Condition Action UPDATE DATABASE File exists in storage only Store the file into the local database. UPDATE STORAGE File exists in database only If the entry is active (not deleted) and not conflicted, restore the file from the database to storage. Deleted entries and conflicted entries are skipped. SYNC DATABASE AND STORAGE File exists in both Compare mtimefreshness. If storage is newer → write to database (STORAGE → DB). If database is newer → restore to storage (STORAGE ← DB). If equal → do nothing. Conflicted documents and files exceeding the size limit are always skipped. -
Initialisation flag — On the very first successful run, writes
initialized = trueto the key-value database so that subsequent runs can restore state in step 2.
Note: mirror does not respect file deletions. If a file is deleted in storage, it will be restored on the next mirror run. To delete a file, use the rm command instead. This is a little inconvenient, but it is intentional behaviour (if we handle this automatically in mirror, we should be against a ton of edge cases).
daemon
daemon is the default command when no command is specified. It runs an initial mirror scan and then continuously syncs changes in both directions:
- CouchDB → local filesystem: via the
_changesfeed (LiveSync mode, default) or periodic polling (--interval N). - local filesystem → CouchDB: via chokidar file watching. Any file created, modified, or deleted in the vault directory is pushed to CouchDB.
In LiveSync mode the _changes feed delivers remote changes as they arrive, with sub-second latency. In polling mode (--interval N) the CLI polls CouchDB every N seconds. Use polling mode if your CouchDB instance does not support long-lived HTTP connections, or if you need predictable network usage.
The daemon exits cleanly on SIGINT or SIGTERM.
# LiveSync mode (default — _changes feed, near-real-time)
livesync-cli /path/to/vault
# Polling mode — poll every 60 seconds
livesync-cli /path/to/vault --interval 60
.livesync/ignore
Place a .livesync/ignore file in your vault root to exclude files from sync in both directions (local → CouchDB and CouchDB → local).
Format:
- Lines beginning with
#are comments. - Blank lines are ignored.
- All other lines are minimatch glob patterns, relative to the vault root.
- The directive
import: .gitignore(exactly this string) reads.gitignorefrom the vault root and merges its non-comment, non-blank lines into the ignore rules. - Negation patterns (lines starting with
!) are not supported and will cause an error on load.
Example .livesync/ignore:
# Ignore temporary files
*.tmp
*.swp
# Ignore build output
build/
dist/
# Merge patterns from .gitignore
import: .gitignore
Patterns apply in both directions: the chokidar watcher will not emit events for matched files, and the isTargetFile filter will exclude them from CouchDB → local sync.
Changes to this file require a daemon restart to take effect.
Planned options:
--immediate: Perform sync after the command (e.g.push,pull,put,rm).serve: Start CLI in server mode, exposing REST APIs for remote, and batch operations.cause-conflicted <vaultPath>: Mark a file as conflicted without changing its content, to trigger conflict resolution in Obsidian.
Use Cases
1. Bootstrap a new headless vault
Create default settings, apply a setup URI, then run one sync cycle.
livesync-cli -- init-settings /data/livesync-settings.json
printf '%s\n' "$SETUP_PASSPHRASE" | livesync-cli -- /data/vault --settings /data/livesync-settings.json setup "$SETUP_URI"
livesync-cli -- /data/vault --settings /data/livesync-settings.json sync
2. Scripted import and export
Push local files into the database from automation, and pull them back for export or backup.
livesync-cli -- /data/vault --settings /data/livesync-settings.json push ./note.md notes/note.md
livesync-cli -- /data/vault --settings /data/livesync-settings.json pull notes/note.md ./exports/note.md
3. Revision inspection and restore
List metadata, find an older revision, then restore it by content (cat-rev) or file output (pull-rev).
livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md
livesync-cli -- /data/vault --settings /data/livesync-settings.json cat-rev notes/note.md 3-abcdef
livesync-cli -- /data/vault --settings /data/livesync-settings.json pull-rev notes/note.md ./restore/note.old.md 3-abcdef
4. Conflict and cleanup workflow
Inspect conflicted revisions, resolve by keeping one revision, then delete obsolete files.
livesync-cli -- /data/vault --settings /data/livesync-settings.json info notes/note.md
livesync-cli -- /data/vault --settings /data/livesync-settings.json resolve notes/note.md 3-abcdef
livesync-cli -- /data/vault --settings /data/livesync-settings.json rm notes/obsolete.md
5. CI smoke test for content round-trip
Validate that put/cat is behaving as expected in a pipeline.
echo "hello-ci" | livesync-cli -- /data/vault --settings /data/livesync-settings.json put ci/test.md
livesync-cli -- /data/vault --settings /data/livesync-settings.json cat ci/test.md
Development
Project Structure
src/apps/cli/
├── commands/ # Command dispatcher and command utilities
│ ├── runCommand.ts
│ ├── runCommand.unit.spec.ts
│ ├── types.ts
│ ├── utils.ts
│ └── utils.unit.spec.ts
├── adapters/ # Node.js FileSystem Adapter
│ ├── NodeConversionAdapter.ts
│ ├── NodeFileSystemAdapter.ts
│ ├── NodePathAdapter.ts
│ ├── NodeStorageAdapter.ts
│ ├── NodeStorageAdapter.unit.spec.ts
│ ├── NodeTypeGuardAdapter.ts
│ ├── NodeTypes.ts
│ └── NodeVaultAdapter.ts
├── lib/
│ └── pouchdb-node.ts
├── managers/ # CLI-specific managers
│ ├── CLIStorageEventManagerAdapter.ts
│ └── StorageEventManagerCLI.ts
├── serviceModules/ # Service modules (ported from main.ts)
│ ├── CLIServiceModules.ts
│ ├── DatabaseFileAccess.ts
│ ├── FileAccessCLI.ts
│ └── ServiceFileAccessImpl.ts
├── services/
│ ├── NodeKeyValueDBService.ts
│ ├── NodeServiceHub.ts
│ └── NodeSettingService.ts
├── test/
│ ├── test-e2e-two-vaults-common.sh
│ ├── test-e2e-two-vaults-matrix.sh
│ ├── test-e2e-two-vaults-with-docker-linux.sh
│ ├── test-push-pull-linux.sh
│ ├── test-setup-put-cat-linux.sh
│ └── test-sync-two-local-databases-linux.sh
├── .gitignore
├── entrypoint.ts # CLI executable entry point (shebang)
├── main.ts # CLI entry point
├── main.unit.spec.ts
├── package.json
├── README.md # This file
├── tsconfig.json
├── util/ # Test and local utility scripts
└── vite.config.ts