19 KiB
Logseq CLI (Node)
The Logseq CLI is a Node.js program compiled from ClojureScript that connects to a db-worker-node server managed by the CLI. When installed, the CLI binary name is logseq.
Build the CLI
npm install -g @vercel/ncc
clojure -M:cljs compile logseq-cli
yarn db-worker-node:release:bundle
yarn db-worker-node:release:bundle compiles and bundles db-worker-node with @vercel/ncc, and writes a standalone runtime to dist/db-worker-node.js plus an asset manifest at dist/db-worker-node-assets.json (which may contain an empty assets array when no extra files are required).
db-worker-node lifecycle
logseq manages db-worker-node automatically. You should not start the server manually. The server binds to localhost on a random port and records that port in the repo lock file.
Desktop + CLI shared semantics:
- Electron desktop and CLI are expected to use the same
db-worker-nodeand lock-file protocol for a graph. - Disk SQLite under
~/logseq/graphsis the source of truth; OPFS periodic export is not part of the desktop primary write path. - If a daemon already exists for the graph, CLI reuses it via lock-file discovery instead of starting a second writer.
- If lock ownership is invalid or stale, startup cleans stale lock state before retrying.
- Lock metadata includes an
owner-sourcevalue (cli,electron,unknown) and lifecycle actions enforce owner boundaries. server stopandserver restartare owner-aware: CLI can only stop/restart servers it owns (or legacyunknownownership).- If lock is missing but a matching orphan
db-worker-nodeprocess still exists for the same repo/data-dir, startup performs orphan cleanup before retrying.
Run the CLI
node ./dist/logseq.js graph list
You can also use npm link to make ./dist/logseq.js globally available, run:
npm link
If installed(or linked) globally, run:
logseq graph list
Configuration
Optional configuration file: ~/logseq/cli.edn
Default data dir: ~/logseq/graphs.
Graph directories on disk are stored as user-facing graph names (for example, demo/), not logseq_db_ prefixed repo identifiers.
Migration note: If you previously used ~/.logseq/cli-graphs or ~/.logseq/cli.edn, pass --data-dir or --config to continue using those locations.
Supported keys include:
:graph:data-dir:timeout-ms:output-format(use:jsonor:ednfor scripting)- sync config persisted via
sync config set|get|unset::ws-url,:http-base,:e2ee-password
cli.edn no longer persists cloud auth tokens. CLI login state is stored separately in ~/logseq/auth.json.
CLI flags take precedence over environment variables, which take precedence over the config file.
Authentication
Use logseq login to authenticate the current machine with Logseq cloud.
logseq loginstarts a temporary callback server athttp://localhost:8765/auth/callback, opens a browser to the Logseq Cognito Hosted UI, exchanges the returned authorization code, and writes~/logseq/auth.json.logseq logoutremoves~/logseq/auth.json, opens a browser to the Cognito Hosted UI logout endpoint, and completes the browser logout flow athttp://localhost:8765/logout-complete.- Sync commands still pass an in-memory runtime
:auth-tokento db-sync, but that token is now resolved fromauth.jsoninstead ofcli.edn.
Default auth file: ~/logseq/auth.json
Auth file contents include the persisted Cognito id-token, access-token, refresh-token, expires-at, sub, email, and updated-at values needed for headless refresh.
Verbose logging:
--verboseenables structured debug logs to stderr for CLI option parsing and db-worker-node API calls.sync downloadcan stream realtime progress lines to stdout when progress is enabled; debug previews remain truncated.
Timeouts:
--timeout-mscontinues to control request timeout behavior for CLI transport.- Login callback timeout is controlled separately by
:login-timeout-ms/LOGSEQ_CLI_LOGIN_TIMEOUT_MSand defaults to 5 minutes. - Logout callback timeout is controlled separately by
:logout-timeout-ms/LOGSEQ_CLI_LOGOUT_TIMEOUT_MSand defaults to 2 minutes.
Commands
Graph commands:
graph list- list all db graphsgraph create --graph <name>- create a new db graph and switch to itgraph switch --graph <name>- switch current graphgraph remove --graph <name>- remove a graphgraph validate --graph <name>- validate graph datagraph info [--graph <name>]- show graph metadata (defaults to current graph)graph export --type edn|sqlite --file <path> [--graph <name>]- export a graph to EDN or SQLitegraph import --type edn|sqlite --input <path> --graph <name>- import a graph from EDN or SQLite (new graph only)
For any command that requires --graph, if the target graph does not exist, the CLI returns graph not exists (except for graph create). graph import fails if the target graph already exists.
Server commands:
server list- list running db-worker-node serversserver status --graph <name>- show server status for a graphserver start --graph <name>- start db-worker-node for a graphserver stop --graph <name>- stop db-worker-node for a graphserver restart --graph <name>- restart db-worker-node for a graphdoctor [--dev-script]- run runtime diagnostics fordb-worker-node.js,data-dirpermissions, and running server readiness (--dev-scriptchecksstatic/db-worker-node.jsexplicitly)
Auth commands:
login- authenticate this machine and create/update~/logseq/auth.jsonlogout- remove persisted CLI auth from~/logseq/auth.json
Shell completion:
completion <zsh|bash>- generate shell completion script to stdout
Setup for zsh (add to ~/.zshrc):
autoload -Uz compinit && compinit
eval "$(logseq completion zsh)"
Setup for bash (add to ~/.bashrc):
eval "$(logseq completion bash)"
Server ownership behavior:
server stopandserver restartcan returnserver-owned-by-otherif the daemon was started by another owner source.server startcan returnserver-start-timeout-orphanwhen lock creation times out and orphan matching processes are detected.server listhuman output includes bothOWNERandREVISIONcolumns.server listprints a compatibility warning in human output when any server revision string is not exactly equal to the local CLI revision string.- Structured output (
--output json|edn) includes per-serverrevisiondata but does not include human warning text.
Sync commands:
sync status --graph <name>- show db-sync runtime state for a graph daemonsync start --graph <name>- start db-sync websocket client for a graphsync stop --graph <name>- stop db-sync client on a graph daemonsync upload --graph <name>- upload local graph snapshot to remotesync download --graph <name> [--progress true|false]- download remote graph<name>into a same-name local graph directorysync remote-graphs [--graph <name>]- list remote graphs visible to the current login contextsync ensure-keys [--graph <name>]- ensure user RSA keys for sync/e2eesync grant-access --graph <name> --graph-id <uuid> --email <email>- grant encrypted graph access to a usersync config set [--graph <name>] ws-url|http-base|e2ee-password <value>- set non-auth db-sync runtime config keysync config get [--graph <name>] ws-url|http-base|e2ee-password- get non-auth db-sync runtime config keysync config unset [--graph <name>] ws-url|http-base|e2ee-password- remove non-auth db-sync runtime config key
Sync upload behavior:
sync uploadrequires--graph <name>.- The CLI starts or reuses that graph's
db-worker-node, applies the current sync config, and uploads the local snapshot only after the worker has resolved a usable remote graph id. - If the local graph already has sync metadata, upload reuses the stored remote
graph-id. - If the local graph does not have a stored remote
graph-id, upload first lists visible remote graphs and reuses an exact same-name match when one exists. - If no same-name remote graph exists, upload creates a new remote graph and persists the returned remote metadata locally before snapshot transfer.
- Successful upload persists graph identity metadata locally in both client-op state and graph KV (
logseq.kv/graph-uuid,logseq.kv/graph-remote?, andlogseq.kv/graph-rtc-e2ee?) so CLI and web upload/bootstrap flows stay aligned. - Fresh uploads default to encrypted remote graph creation unless local sync metadata explicitly marks the graph as non-e2ee. In headless CLI mode, run
logseq loginfirst and sete2ee-passwordviasync config set(or in--config) before uploading encrypted graphs. sync uploadreturns a real error instead of false success when login state, remote graph bootstrap, or snapshot upload fails.- Common upload failures include missing/invalid CLI login state, missing
http-base, remote graph creation failure, snapshot upload failure, and local DB/worker startup failure. - Troubleshooting: after a successful upload, run
graph info --graph <name> --output jsonand confirmdata.kv.logseq.kv/graph-uuidis present. If it is missing, rerunsync uploadfor the same graph to trigger identity backfill.
Sync download behavior:
sync downloadrequires--graph <name>.- If a local graph with the same name already exists, the CLI returns
graph-exists. - If no remote graph with that name exists, the CLI returns
remote-graph-not-found. sync downloadstartsdb-worker-nodein create-empty mode so local startup does not writedb-initial-databefore snapshot import.- The final snapshot download/import invoke uses a command-specific long-running timeout (30 minutes by default) rather than the generic short-request timeout path.
- Progress streaming uses db-worker-node SSE
/v1/eventsand shared:rtc.log/downloadevents. --progressdefaults totruefor human output.- For structured output (
--output json|edn), progress is auto-disabled unless explicitly overridden with--progress true. --progress falsealways suppresses progress streaming.- If the target graph DB is not empty at download time, the CLI returns
graph-db-not-emptyand aborts before import. - For e2ee remote graphs in headless CLI mode, run
logseq loginfirst and sete2ee-passwordviasync config set(or in--config) before download.
Sync config persistence:
sync config set/unsetwrites non-auth sync config to the CLI config file selected by--config.- If
--configis not provided, the default config path is~/logseq/cli.edn. sync config getreads from that same config source.- Cloud auth is persisted separately in
~/logseq/auth.json.
Inspect and edit commands:
list page [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]- list pageslist tag [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]- list tagslist property [--expand] [--limit <n>] [--offset <n>] [--sort <field>] [--order asc|desc]- list properties (TYPEis included by default even without--expand)upsert block --content <text> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]- create blocks; defaults to today’s journal page if no target is givenupsert block --blocks <edn> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]- insert blocks via EDN vectorupsert block --blocks-file <path> [--target-page <name>|--target-id <id>|--target-uuid <uuid>] [--pos first-child|last-child|sibling]- insert blocks from an EDN fileupsert block --id <id>|--uuid <uuid> [--target-id <id>|--target-uuid <uuid>|--target-page <name>] [--pos first-child|last-child|sibling] [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]- update and/or move a blockupsert page --page <name> [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]- create (or update by page name) a pageupsert page --id <id> [--update-tags <edn-vector>] [--update-properties <edn-map>] [--remove-tags <edn-vector>] [--remove-properties <edn-vector>]- update a page by id (cannot be combined with--page)upsert tag --name <name>- create or upsert a tag by nameupsert tag --id <id> [--name <name>]- validate a tag by id; when--nameis provided, rename that tag id (no-op if normalized name is unchanged)upsert tag --id <id> --name <name>conflicts: returnstag-name-conflictwhen target name is a non-tag page, andtag-rename-conflictwhen target name is another existing tagupsert property --name <name> [--type <type>] [--cardinality one|many] [--hide true|false] [--public true|false]- create or update a property by nameupsert property --id <id> [--type <type>] [--cardinality one|many] [--hide true|false] [--public true|false]- update a property by idmove --id <id>|--uuid <uuid> --target-id <id>|--target-uuid <uuid>|--target-page <name> [--pos first-child|last-child|sibling]- move a block and its children (defaults to first-child)remove --id <id>|--uuid <uuid>|--page <name>- remove blocks (by db/id or UUID) or pagessearch <query> [--type page|block|tag|property|all] [--tag <name>] [--case-sensitive] [--sort updated-at|created-at] [--order asc|desc]- search across pages, blocks, tags, and properties (query is positional)query --query <edn> [--inputs <edn-vector>]- run a Datascript query against the graphquery --name <query-name> [--inputs <edn-vector>]- run a named query (built-in or fromcli.edn)query list- list available named queriesshow --page <name> [--level <n>]- show page treeshow --uuid <uuid> [--level <n>]- show block treeshow --id <id> [--level <n>]- show block tree by db/id
Help output:
Subcommands:
list page [options] List pages
list tag [options] List tags
list property [options] List properties
upsert block [options] Upsert block
upsert page [options] Upsert page
upsert tag [options] Upsert tag
upsert property [options] Upsert property
move [options] Move block
remove [options] Remove block or page
search <query> [options] Search graph
show [options] Show tree
Options grouping:
- Help output separates Global options (apply to all commands) and Command options (command-specific flags).
Version output:
logseq --versionprints:
Build time: <timestamp>
Revision: <commit>
Output formats:
- Global
--output <human|json|edn>applies to all commands - Output formatting is controlled via global
--output,:output-formatin config, orLOGSEQ_CLI_OUTPUT. - Human output is plain text. List/search commands render tables with a final
Count: Nline. For list and search subcommands, the ID column uses:db/id(not UUID). If:db/identexists, anIDENTcolumn is included.list propertyincludes a dedicatedTYPEcolumn. Search table columns areIDandTITLE. Block titles can include multiple lines; multi-line rows align additional lines under theTITLEcolumn. Times such as listUPDATED-AT/CREATED-ATandgraph infoCreated atare shown in human-friendly relative form. Errors include error codes and may include aHint:line. Use--output json|ednfor structured output. sync downloadprogress lines are streamed to stdout only when progress is enabled. Injson/ednmode, progress is disabled by default unless--progress trueis provided.- For
list property,TYPEis returned in default output (without--expand) for human and structured (json/edn) formats. upsert pageandupsert blockreturn entity ids indata.resultfor JSON/EDN output, and include ids in human output.- Human example:
Upserted page: [123] - Human example:
Upserted blocks: [201 202] - JSON example:
{"status":"ok","data":{"result":[123]}} - EDN example:
{:status :ok, :data {:result [123]}}
- Human example:
doctoroutput includes overall status (ok,warning,error) and per-check rows fordb-worker-script,data-dir,running-servers, andserver-revision-mismatch. For scripting,--output json|ednkeeps the structured check payload.- Common doctor failures and warnings:
doctor-script-missing:db-worker-node.jsruntime target is missing (default target:dist/db-worker-node.js; usedoctor --dev-scriptto checkstatic/db-worker-node.js).doctor-script-unreadable: script path exists but is not a readable file.data-dir-permission: configured data dir is not readable or writable.doctor-server-not-ready: one or more lock-discovered servers are still in:startingstate (warning).doctor-server-revision-mismatch: one or more discovered servers use a different revision than the local CLI revision (warning). Follow the printed remediation command for each affected graph:logseq server restart --graph <name>.- If bundled runtime startup fails with missing-module or missing-file errors, rebuild with
yarn db-worker-node:release:bundleand confirmdist/db-worker-node.jsexists and every path listed indist/db-worker-node-assets.jsonis present next to it.
queryhuman output returns a plain string (the query result rendered viapr-str), which is convenient for pipelines likelogseq query ... | xargs logseq show --id.- Built-in named queries currently include
block-search,task-search,recent-updated,list-status, andlist-priority. Usequery listto see the full set for your config. - Show and search outputs resolve block reference UUIDs inside text, replacing
[[<uuid>]]with the referenced block content. Nested references are resolved recursively up to 10 levels to avoid excessive expansion. For example:[[<uuid1>]]→[[some text [[<uuid2>]]]]and then<uuid2>is also replaced. showhuman output prints the:db/idas the first column followed by a tree:
id1 block1
id2 ├── b2
id3 │ └── b3
id4 ├── b4
id5 │ ├── b5
id6 │ │ └── b6
id7 │ └── b7
id8 └── b8
Troubleshooting:
- If authenticated sync commands fail with missing or invalid local auth, run
logseq logoutand thenlogseq loginagain. - You can also manually remove
~/logseq/auth.jsonand repeatlogseq login.
Examples:
node ./dist/logseq.js login
node ./dist/logseq.js graph create --graph demo
node ./dist/logseq.js graph export --type edn --file /tmp/demo.edn --graph demo
node ./dist/logseq.js graph import --type edn --input /tmp/demo.edn --graph demo-import
node ./dist/logseq.js upsert block --target-page TestPage --content "hello world"
node ./dist/logseq.js move --uuid <uuid> --target-page TargetPage
node ./dist/logseq.js search "hello"
node ./dist/logseq.js show --page TestPage --output json
node ./dist/logseq.js server list
node ./dist/logseq.js doctor
node ./dist/logseq.js doctor --dev-script
node ./dist/logseq.js doctor --output json
node ./dist/logseq.js logout