This is the first part of a 3 part series:

  1. Introduction, Logseq and Termux on mobile (this part)
  2. Improving Logseq and Org-roam interoperability
  3. Automating synchronisation

I started to use Logseq in the fall of 2023, and with almost 9 months of use, I’m confident it is a great mobile application for Org-roam. Some applications do a better job at being compatible with the Org file format (e.g. organice) however they do not support Org-roam readily. Logseq, while a bit lax with its support of the Org file format (more on that in a bit), is closer to Org-roam’s Zettelkasten philosophy: links between pages are a breeze to create, backlinks show up immediately and the journal entries quickly becomes your entry point for time management.

In this post, I’ll go over my mobile setup and some of the quirks of using Logseq with Org-roam. I’ll follow up on my Emacs setup and automating all this in future posts. Let’s start by understanding their differences.

Logseq & org-roam interoperability

The good

Logseq was deliberately designed for the minimal interface required for web/mobile applications. Capture is both user-friendly and functional.

As an Org editor and interface, it is impressive. It supports heading highlights for tasks, checkboxes, tags. It renders quote blocks, code blocks (with syntax highlighting), properties, keywords, tables, etc. It also supports inline \(\LaTeX\) flawlessly, putting my Emacs setup to shame.

Figure 1: Logseq

Figure 1: Logseq

Performance-wise, Logseq easily handles the thousands of notes that I have without noticeable penalty. Its Org-agenda equivalents (queries) render their results almost immediately.

Logseq also has templates. Granted, the format to create a template is different and your Yasnippets won’t work, but this feature is still invaluable to ease input on a phone.

Logseq has even more features making you love it other ways, and it has definitely influenced my Org-roam setup, for the better.

The bad

Using Logseq and Org-roam together imposes some constraints on how you organise your notes. I’ve come to prefer Logseq’s approach over my own, inherited from Org, but it might not be your cup of tea. Logseq imposes the following directory structure for your “graph” (i.e. org-roam-directory):

└── org-roam-directory/
    ├── assets/
    ├── journals/
    ├── logseq/
    └── pages/
  • The assets directory is where Logseq stores attachments, thus it is handy to map it to your org-attach-id-dir.
  • The journals directory is where Logseq stores your dailies, and should map to your org-roam-dailies-directory.
  • The logseq directory is Logseq’s own internal folder, and it must be hidden from Org-roam in org-roam-file-exclude-regexp.
  • The pages directory is where Logseq stores every single note. The lack of hierarchy for pages may not suit everyone. I find personally that Org-roam tagging provides more flexibility, so it’s not an issue for me.

Within a file, Logseq insists on hierarchical note-taking and does not handle well the top section of an Org file. It will shove top section paragraphs, top node properties and keywords under a heading… Which you should not try to edit.

Also, some pages have special meaning in Logseq. For example, you will need to port your Yasnippets to the “Templates” page if you need them in Logseg. If you want to see all your to-dos, you’ll need to visit the “TODO” page, where content gets generated on the fly.

This is not the only thing Logseq’s generates on the fly: results of Logseq queries are generated on the fly too, and the result is not stored in the file, unlike what Babel does. And since Logseq’s does not support Babel, queries are not interoperable between the 2 platforms.

The ugly

The biggest problem however is that Logseq uses Org internal link syntax for external links. This is an Org internal link:

* Heading A
* Heading B internal link to [[Heading A]]

As the name suggests, internal links are looked up only within the same file in Org. A link to another Org file is an called external link, and they have a different syntax:

* External file link to [[file:other.org][another note]]
* External ID link to [[id:some-id][another note]]

Logseq uses Org’s internal link syntax by default, which may conflict with Org. There’s an option to force Logseq to use file paths instead. Unfortunately, this is buggy (both version 0.9.x and 0.10.x) when a note does not exists yet.

And ultimately, Org-roam uses ID links.

Regrettably, when converting Org links to ID links (as Org-roam usually does) it breaks the link relationship for Logseq. Logseq can still follow the link, but the backlink will appear under “Unlinked references”. This unfortunately breaks the graph in Logseq: all your notes suddenly look isolated. If you really enjoy the graph visualisation in Logseq, this could be a deal-breaker.

So while Logseq does look like a good companion for Org-roam, it does not care about Org-roam. Logseq will not add property ID your notes, it won’t use Org-roam aliases, and it will not convert links to ID links.

Why use Logseq with Org-roam then?

To address some of these issues, I (and others) have written Emacs packages that I’ll present in my next post. With these packages, the ugly issues disappear and only some of the bad remains. Thus Logseq shines with what it does well, and it is really good at what it does well.

Even without these packages, the Zettelkasten philosophy, its usability and all of its functionalities really make up for the remaining quirks. You can start using Logseq with Org-roam to try it, if only as a viewer.

If you’re convinced, let’s move on to configuring Termux for git synchronisation on your phone.

Setting up Termux and Git

I synchronise my notes with git. There’s easier ways to doing it, but I don’t think there’s much better ways than using an SCM. All my notes are on a private repository. This section only applies to Android, I am sure there are similar methods for iOS, though I don’t know them.

Firstly, install the excellent Termux app on your phone. Once done, you can open the app and simply:

apt update
apt upgrade
pkg install git

During the upgrade process, you may sometimes have to answer the question: Configuration file ... The default action is to keep your current version. (Y/I/N/O/D/Z) [default=N] ? Just type I since it’s a fresh install. I also recommend picking an editor of your choice if you do not like nano. I find nano is a good fit on a phone. This will come in handy when you have merge conflicts to resolve.

Then type termux-setup-storage and grant Termux access to your shared storage (i.e. user data storage, SD card).

I placed my notes at the root of my phone’s shared storage, which you can access from your Termux user at ~/storage/shared/Notes:

mkdir storage/shared/Notes
ln -s storage/shared/Notes
cd Notes
git config --global --add safe.directory ~/storage/shared/Notes
git config --global user.name ${name}
git config --global user.email ${email}

Line 4 is required since the user performing the git commands (you) is different from the user owning the git repository. I believe this is due to the fuse abstraction on top of the shared storage’s file system but haven’t dug into it. Onto SSH now.

pkg install openssh

The installation process should prompt you to create a default SSH key. If it does not, once the installation is complete, just:

ssh-keygen

I recommend you do not set a passphrase for this key. Unlike your desktop or laptop, Android does not come with a standard keychain application, so you’ll have to enter your passphrase every time you need to use it or rely on third-party applications. It’s a significant hassle in one case, or an issue of trust in the other. For third-party applications, you could explore using OpenKeychain together with OkcAgent, though I can’t recommend it given that OkcAgent’s sole maintainer does not seem to have time to work on it.

For security, you should only use this key for the single purpose of synchronising your notes, and you should prefer to rotate it regularly; I do it every 6 months. I personally use GitLab for the unlimited number of private repositories, which also allows me to set an expiration date on the key, forcing me to rotate the key. After adding your key to your SCM, try:

cd ~/Notes
git clone git@${remote-url}:${user-name}/${repository}.git .
git config pull.rebase false

Since this is a repository for synchronisation of text content; you will save yourself a lot of hassle by letting git pick the best merge strategies for you.

We’re going to make our first change on the phone, by telling git to ignore Logseq’s backup data:

echo "logseq/bak" >> .gitignore
echo "logseq/.recycle" >> .gitignore
git commit -a -m "Ignore Logseq cache"
git push

Logseq installation & configuration

Grab the latest version of Logseq here: https://github.com/logseq/logseq/releases. You can go with the Nightly if you feel adventurous.

Once installed, Logseq will present you with a bare-bone graph. Load your Org-roam notes by finding the graph button (with the database icon) in the side-panel and select “Add new graph”. Now pick the directory containing your recently cloned repository.

Once loaded and indexed, go straight to the configuration of the graph: “\(\cdot\cdot\cdot\)” → “Settings” → “Edit config.edn”. You want to modify/set the following fields, or at least take note of them:

:preferred-format :org             ;; required!
:pages-directory "pages"           ;; default
:journals-directory "journals"     ;; default, must match `org-roam-dailies-directory`
:journal/page-title-format "yyyy-MM-dd"   ;; match with `org-roam-dailies-capture-templates`
:journal/file-name-format "yyyy-MM-dd"    ;; match with `org-roam-dailies-capture-templates`
:preferred-workflow :todo          ;; recommended
:property-pages/enabled? false     ;; recommended, disable property pages

You will need to review some of your configuration in Emacs, and maybe move some files around, if you want Logseq to handle them. A minimal corresponding Org-roam configuration to the above could look like:

(setq org-directory "~/Notes/"
      org-roam-directory (file-truename (file-name-concat org-directory "roam/"))
      org-roam-dailies-directory "journals/")
(setq org-roam-file-exclude-regexp "\\.git/.*\\|logseq/.*$"
      org-roam-capture-templates
      '(("d" "default" plain
         "%?"
         ;; Accomodates for the fact that Logseq uses the "pages" directory
         :target (file+head "pages/${slug}.org" "#+title: ${title}\n")
         :unnarrowed t))
      org-roam-dailies-capture-templates
      '(("d" "default" entry
         "* %?"
         :target (file+head "%<%Y-%m-%d>.org" ;; format matches Logseq
                            "#+title: %<%Y-%m-%d>\n"))))

Pay attention to org-roam-file-exclude-regexp which should exclude the logseq directory. Once you have updated your Logseq (and possibly Emacs) configuration, don’t forget to push the changes to your repository.

Start exploring!

You’re now minimally setup to use Logseq and Org-roam together and you can start playing with them and see how it feels to take your Org-roam notes on the go with Logseq.

In subsequent posts, I’ll explain how I’ve configured Emacs in details, and present packages I’ve written to improve interoperability between Logseq and Org-roam.