A command-line client for Obsidian Sync that lets you download, upload, and inspect vault contents without the Obsidian app.
Built in Go. Supports end-to-end encrypted vaults.
go install github.com/wojas/ob1/cmd/ob1@latestRequires Go 1.25 or later.
# Authenticate (interactive prompt for email/password/MFA)
ob1 login
# List your vaults and pick one to set up locally
ob1 vault list
ob1 vault setup <vault-id>
# Inspect and download
ob1 status # compare local files with the vault
ob1 list # list all remote files
ob1 cat path/to/note.md # print a file to stdout
ob1 get path/to/note.md # download specific files
ob1 get --merge note.md # merge text changes when possible
ob1 pull --only-notes # download all .md files
ob1 pull --delete-unknown # mirror remote deletions for non-hidden files
# Upload
ob1 put path/to/note.md # upload specific files
ob1 experimental mv note.md archive/note.md # experimental rename/move (see warning below)
ob1 rm path/to/note.md # remove remote + local copy (local backup by default)Running ob1 vault setup in a directory creates a .ob1/ folder that ties that directory to your vault. All subsequent commands operate relative to this directory.
| Command | Description |
|---|---|
login |
Sign in to your Obsidian account. Accepts --email, --password, --mfa flags or prompts interactively. |
logout |
Sign out and delete the local session token. |
info |
Print account metadata. |
vault list |
List all vaults you own or have access to. |
vault setup <id> |
Configure the current directory for a specific vault. Prompts for your vault encryption password. |
list |
List all files in the remote vault. Use --cached to read from the local cache without contacting the server. |
status |
Compare local files with the remote vault in a git status style view. Use -a to include files that are already in sync. |
cat <path> |
Print a remote file's contents to stdout. |
get <file> [...] |
Download specific files. Skips files that are already up to date locally. Use --merge to merge text changes instead of overwriting. |
pull |
Download all remote files. Use --only-notes to limit to .md files, --merge to merge text changes, and --delete-unknown to remove non-hidden local files that no longer exist remotely. Existing files are backed up before overwrite or deletion unless --no-backup is set. |
put <file> [...] |
Upload local files to the vault. Skips unchanged files. |
experimental |
Experimental commands with known sync bugs. Do not use on production data. |
experimental mv <source> <destination> |
Move or rename a remote file and its local copy. Destination must include a filename (directory paths like archive/ are rejected). If the local destination exists, it is backed up by default; pass --no-backup to disable backups. Folders are not supported yet. |
rm <file> [...] |
Remove remote files from the vault and remove local copies. Local files are backed up by default; pass --no-backup to disable backups. Folders are not supported yet. |
All commands support --debug for request and protocol logging, --no-cache to bypass the local snapshot cache, and --dry-run to show what would change without making local changes. status also supports -v for human-readable explanations.
ob1 experimental ...commands have known sync bugs and are not safe for production data.- For agent workflows, prefer manual rename semantics:
cp old/path.md new/path.md
ob1 put new/path.md
ob1 rm old/path.md| Path | Contents |
|---|---|
~/.ob1/user.json |
Session token (user-level, shared across vaults) |
.ob1/vault.json |
Vault config: ID, host, encryption key, device name, and sync state |
.ob1/cache.json |
Cached remote file listing for faster list and status |
.ob1/base/ |
Merge bases used by get --merge and pull --merge |
.ob1/backup/ |
Backups created by pull before overwriting or deleting local files |
All credential files are written with mode 0600.
- Encrypted vault setup requires a password-based vault with an exposed salt. Managed-encryption (Obsidian-hosted key) vaults are not yet supported.
- Merge support is incomplete.
--mergeonly applies to text files, depends ondiff3, and uses locally stored merge bases. If no base exists yet,ob1falls back to standard two-way conflict markers instead of a full three-way merge. - Binary conflicts are not merged. If a binary file changed both locally and remotely,
ob1overwrites the local copy duringpull, with a backup by default. - There is no continuous sync loop. Sync is explicit: you run commands such as
pull,get, andputyourself. - History and restore flows are not exposed yet. The client does not currently let you browse or recover older remote revisions from the CLI.
Contributions are welcome! For bug fixes and small improvements, go ahead and open a PR. For refactoring or larger changes, please open an issue first to discuss the approach.
AI-assisted contributions are welcome. If you used AI tools, please verify and test all changes yourself before submitting, and mention the models used in your PR description.
MIT. See LICENSE.
