All of ekg's data is stored in a sqlite database. Notes are organized around tags, and you can view many notes by looking at one or more tags.
[[./screenshots/ekg-tag-view.jpg]]
Editing a note from one of these buffers, or capturing a new note, lets you edit both the note's text content and its metadata (mostly tags you add to it) in a separate buffer.
[[./screenshots/ekg-edit.jpg]]
ekg has functionality to have notes created with templates that vary based on the tags you are using. It also supports saving unfinished notes as drafts.
Notes can be on file resources and web resources. You can use notes on file resources to have a stored annotation for files.
ekg supports import from org-roam, and import and export to logseq. It also supports inline commands, which can be used for transclusion and any other functionality you may need.
There is support for attaching Large Language Model (LLM) "embeddings" to notes, for use in search and similarity search, via the llm package. This allows you to search based on semantics, as opposed to text matching. You can also use LLM chat in your notes, getting an LLM to respond to your notes based on a default prompt, or new prompts that you add.
Additionally, [[https://youtu.be/qxa2VrseFUA][a video demonstration and explanation of ekg]] is on YouTube. This README has only the most basic information, but the full information can be found in the [[https://github.com/ahyatt/ekg/blob/develop/doc/ekg.org][manual]], which can be viewed in the Emacs info after installing.
The advantage of this method is that it solves something that has bothered me for a while about the recent suite of tools like org-roam: backlinks are non-symmetrical. If you enter a note in your org-roam daily about emacs, and link it to the emacs note, then when you go to the emacs note, you have to explicitly enable the backlinks buffer to see the daily entry where you first entered it. Systems such as Logseq and the original Roam have backlinks alongside normal content, but this doesn't seem possible in emacs, where a buffer of a file is expected show the file, and tricks with overlays can't solve the issue. Even if it could, I want a system in which it doesn't matter where you enter the data, it shows up in the original place the same as everywhere else it is linked to, not as a backlink, but just as part of the content. Having notes with no title, only tags, makes this possible, because there is no longer a difference between linking and writing the context, both are denoted by tags.
As a consequence of this design, notes can be small, because to add another note to a subject, you don't need to append to an existing note, you can create another note.
Additionally, ekg has another key difference: it uses =sqlite= instead of the filesystem. When notes are small and do not have titles, files don't make a lot of sense anymore. Additionally, the filesystem is limited. Even in org-roam, which uses it, it needs to be augmented with sqlite anyway to enable fast querying of tags and other operations. The sqlite-only approach also means it is much easier to make certain kinds of changes, since they only involve changing the database and not the text as well. In general, text and data are separated as much as possible here, so there's no need or desire for the text to have to store data as well, we leave that completely to the database.
The benefit of this is that it's now possible to narrow in on just tags of a certain type if necessary.
There are a few other types of prefixes commonly used for tags. One is that titled resources have default tags that are prefixed with "doc/", followed by the name of the document.
Functionality such as templates and LLM prompting can work with prefixed tags, so when looking for templates or prompts for a tag with some hierarchy, ekg will look at the tag hierarchy from the top to the bottom to add any relevant information.
Emacs versions prior to version 29 are dependent on =emacsql= to provide sqlite functionality, which itself has a dependency on having a =sqlite= binary available on your system.
An example installation using =use-package= is below:
(use-package ekg :bind (([f11] . ekg-capture)))
If you are using embedding and llm functionality, an example in which you use Open AI's API is the following:
(use-package ekg :bind (([f11] . ekg-capture)) :init (require 'ekg-embedding) (ekg-embedding-generate-on-save) (require 'ekg-llm) (require 'llm-openai) ;; The specific provider you are using must be loaded. (let ((my-provider (make-llm-openai :key "my-openai-api-key"))) (setq ekg-llm-provider my-provider ekg-embedding-provider my-provider)))
Note: make sure to never check in an API key, so if you check in your configuration, set up your API key in a loaded elisp file you do not check in.
If you'd like to enable auto-saving while editing:
(use-package ekg :config (require 'ekg-auto-save) (add-hook 'ekg-capture-mode-hook #'ekg-auto-save-mode) (add-hook 'ekg-edit-mode-hook #'ekg-auto-save-mode))
At the top of the note is a special section, which will be unfamiliar. This is the metadata section, where it stores and displays editable metadata, notably tags, but possibly other data such as URLs. If you want to change tags, just edit them in the metadata section. Tags will autocomplete based on the tags you've already created.
The basic read operation in ekg is to show a list of notes according to some rule. An example is =ekg-show-notes-with-tag=. The notes displayed can be navigated between using =n= and =p=, and interacted with. The following section has a complete list of commands and keybindings.
| Command | Description | |--------------------------------+---------------------------------------------------------------------------| | =ekg-capture= | Capture a new note | | =ekg-capture-url= | Capture a new note (or open an existing note) about a URL | | =ekg-capture-file= | Capture a new note (or open an existing note) about a file | | =ekg-show-notes-with-tag= | Open a buffer for notes matching the single tag given | | =ekg-show-notes-with-any-tags= | Open a buffer for notes matching any of the given tags | | =ekg-show-notes-with-all-tags= | Open a buffer for notes matching all of the given tags | | =ekg-show-notes-with-tag-prefix= | Open a buffer for notes matching any tag with a tag with the given prefix | | =ekg-show-notes-in-trash= | Open a tag buffer that shows all notes in the trash | | =ekg-show-notes-in-drafts= | Open a tag buffer that shows all draft notes (saved but not finalized) | | =ekg-show-notes-for-today= | Open a tag buffer that shows notes with today's tag | | =ekg-show-notes-latest-captured= | Open a buffer that shows the latest notes that have been captured | | =ekg-show-notes-latest-modified= | Open a buffer that shows the latest notes that have been modified | | =ekg-browse-url= | Open a URL stored as a resource to a note, completing by note title |
These are also global commands, but for more occasional or specialized uses:
| Command | Description | |-------------------------+------------------------------------------------------------| | =ekg-global-rename-tag= | Rename a tag, updating all references to it | | =ekg-upgrade-db= | After upgrading, update any obsoletely stored data | | =ekg-clean-db= | Remove unused data from the database, including empty tags |
Commands relevant to capture buffers:
| Command | Description | |------------------------+--------------------------------------| | =ekg-change-mode= | Change note major-mode | | =ekg-capture-finalize= | Finish and save (bound to =C-c C-c=) | | =ekg-capture-abort= | Trash the note (bound to =C-c C-k=) |
Commands relevant to edit buffers:
| Command | Description | |---------------------+--------------------------------------| | =ekg-change-mode= | Change note major-mode | | =ekg-edit-abort= | Abort all edits (bound to =C-c C-k=) | | =ekg-edit-finalize= | Finish and save (bound to =C-c C-c=) |
Commands relevant to note view buffers:
| Command | Description | Binding | |-----------------------------------+---------------------------------------------------------------------------+---------| | =ekg-notes-tag= | Open another tag buffer selecting from tags of current note | =t= | | =ekg-notes-open= | Edit the currently selected note | =o= | | =ekg-notes-delete= | Delete the currently selected note | =d= | | =ekg-notes-browse= | Open the resource, if one exists | =b= | | =ekg-notes-select-and-browse-url= | Select from the URLs in the current note buffer, and browse. | =B= | | =ekg-notes-refresh= | Refresh the tag, refetching all the data displayed | =g= | | =ekg-notes-create= | Add a note with all the tags displayed in the buffer | =c= | | =ekg-notes-next= | Move selection to the next note | =n= | | =ekg-notes-previous= | Move selection to the previous note | =p= | | =ekg-notes-any-note-tags= | Open another tag buffer showing any of the tags in the current note | =a= | | =ekg-notes-any-tags= | Open another tag buffer showing any of the tags in any note in the buffer | =A= | | =ekg-notes-kill= | Kill a note from the current view (does not change the database) | =k= |