Internals
The NoteWriter is fundamentally a CLI to extract objects from Markdown files.
The NoteWriter Objects
When running commands like nt add
, The NoteWriter parses Mardown files to extract different objects.
file
Each parsed file generates an object file
representing the file as a YAML document.
Ex (Mardown):
---tags:- go---
# Go
## Note: Golang History
`#history`
`@source: https://en.wikipedia.org/wiki/Go_(programming_language)`
[Golang](https://go.dev/doc/ "#go/go") was designed by Robert Greisemer, Rob Pike,and Ken Thompson at Google in 2007.
## Flashcard: Golang Logo
What does the **Golang logo** represent?
---
A **gopher**.

## TODO: Conferences
* [Gophercon Europe](https://gophercon.eu/) `#reminder-2023-06-26`
Ex (Internal):
oid: 6347cbdd # A uniquely generated OIDrelative_path: go.md # Relative file path from the root of the repositorywikilink: go # Long wikilink (ex: [[go]] is a valid link to this file)front_matter: null # The front matter in YAMLattributes: # The file attributes (including optional tags) tags: - gobody: |- # The raw body (without the Front Matter) # Go ...body_line: 6 # The line number of the first body linesize: 463 # The raw file sizzehash: 1eba21ae87635b6b9a76ca4df89bf2931da64d42 # A SHA-1 using file content as sourcemtime: 2023-01-01T12:00:00 # The last modified time on FScreated_at: 2023-01-01T12:00:00 # The file object creation timeupdated_at: 2023-01-01T12:00:00 # The file object modification time
note
Each note, independent of its type, generates an object note
representing the note as a YAML document.
Ex (Markdown):
## Note: Golang History
`#history`
`@source: https://en.wikipedia.org/wiki/Go_(programming_language)`
[Golang](https://go.dev/doc/ "#go/go") was designed by Robert Greisemer, Rob Pike,and Ken Thompson at Google in 2007.
Ex (Internal):
oid: d790d08c # Unique generated OIDfile_oid: 6347cbdd # OID of the file containing this noteparent_note_oid: "" # OID of the parent heading when nested under a notetype: Note # Type of the objecttitle: 'Note: Golang History' # Raw Title without the heading character(s) "#"short_title: Golang History # Title without the type prefixlong_title: Golang History # Concatenation with all optional parent short titlesrelative_path: go.md # Relative path of the file containingg this notewikilink: 'go#Note: Golang History' # Long wikilink (ex: [[go#Note: Golang History]] is a valid link)attributes: # Attributes (including inherited ones) source: https://en.wikipedia.org/wiki/Go_(programming_language) tags: - go - history title: Golang Historytags: # Tags (= special attribute named "tags") - go - historyline: 8 # Line number of the first line inside the filecontent: |- # Processed content `#history`
`@source: https://en.wikipedia.org/wiki/Go_(programming_language)`
[Golang](https://go.dev/doc/ "#go/go") was designed by Robert Greisemer, Rob Pike, and Ken Thompson at Google in 2007.content_hash: 0eba86c8b008c0222869ef5358d48ab8241ffc8e # SHA-1 of the property content_rawcreated_at: 2023-01-01T12:00:00 # Object creation timeupdated_at: 2023-01-01T12:00:00 # Object modification time
flashcard
Each note of type flashcard
generates an additional object flashcard
representing the flashcard to learn as a YAML document.
Ex (Markdown):
## Flashcard: Golang Logo
What does the **Golang logo** represent?
---
A **gopher**.

Ex (Internal):
oid: 3c268dd8 # Unique generated OIDshort_title: Golang Logo # Short title of the notefile_oid: 6347cbdd # File OID containing the notenote_oid: 3513929a # Note OID associated with this flashcardrelative_path: go.md # Relative path to the filetags: [go] # Tags (= special attributes "tags")interval: 1 # Various SRS settings...front: |- # Front card in Markdown What does the **Golang logo** represent?back: |- # Back card in Markdown A **gopher**.
created_at: 2023-01-01T12:00:00 # Object creation timeupdated_at: 2023-01-01T12:00:00 # Object modification time
media
Each link inside note
referencing a local file generates an object media
representing the media file as a YAML document.
Ex (Markdown):

Ex (Internal):
oid: 840dd3bc # Unique generated OIDrelative_path: medias/go.png # Relative path to root directory of the repositorykind: picture # Object kinddangling: false # True when file is not present on diskextension: .png # File extensionmtime: 2023-01-01T12:00:00 # File last modification time on FShash: ef81045f57ea747457769965487ac8211a44ed32 # SHA-1 using file content as sourcesize: 14220 # File size in bytesblobs: # List of blobs (= optimized versions) - oid: 6545e323 # Unique OID using file content mime: image/avif # Mime type attributes: {} # Optional attributes tags: # Identify the blob type - preview # (preview = thumbnail) - lossy # (lossy = lossy conversion) - oid: eb49431b mime: image/avif attributes: {} tags: - original - lossycreated_at: 2023-01-01T12:00:00 # Object creation timeupdated_at: 2023-01-01T12:00:00 # Object modification time
link
Each link (internal or external) that includes a Go link inside the link’s title generates an additional object link
representing the special link as a YAML document.
Ex (Markdown):
[Golang](https://go.dev/doc/ "#go/go") was designed by Robert Greisemer, Rob Pike, and Ken Thompson at Google in 2007.
Ex (Internal):
oid: 8c9d3ed # Unique generated OIDnote_oid: d790d08c # Note OID containing the link definitionrelative_path: go.md # Relative path to the file containing the linktext: Golang # Link Texturl: https://go.dev/doc/ # Link target URLtitle: "" # Link title without the special syntax for the Go linkgo_name: go # Name of the Go linkcreated_at: 2023-01-01T12:00:00 # Object creation timeupdated_at: 2023-01-01T12:00:00 # Object modification time
reminder
Reminders are defined using a special tag syntax and generate additional reminder
object representing the reminders as YAML document:
Ex (Markdown):
* [Gophercon Europe](https://gophercon.eu/) `#reminder-2023-06-26`
Ex (Internal):
oid: 9032a26e # Unique generated OIDfile_oid: 6347cbdd # File OID containing this remindernote_oid: 9d5ac892 # Note OID containing this reminderrelative_path: go.md # Relative path to the file containing this reminderdescription: |- # The description of reminder '[Gophercon Europe](https://gophercon.eu/)'tag: '#reminder-2023-06-26' # The tagcreated_at: 2023-01-01T12:00:00 # Object creation timeupdated_at: 2023-01-01T12:00:00 # Object modification time
The NoteWriter Database
Above objects are stored (indirectly) in the index. Ex:
.├── .nt│ ├── .gitignore│ ├── config│ ├── database.db│ ├── index│ └── objects│ ├── 4a│ │ └── 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6.pack│ ├── af│ │ └── afe988e5f40e4d1181a86f522e3c1f2e6f0241e3.pack│ ├── db│ │ └── dbeaba7026ce4be8aa84ee85992dc9eca31118f7.blob│ └── e7│ └── e7b26e89367a22b5f578532a74742e646f843e1f.blob│├── .ntignore├── hello.md└── me.png
This section documents the format of the different files that composed the internal database.
.nt/objects/
This directory contains two kinds of objects:
- Pack Files (
*.pack
): A group of objects present in a single file inside the repository (a Markdown file, a media file). - Blobs (
*.blob
): The raw bytes for a single media file, the metadata are stored in the media object referencing the blob inside a commit object.
All objects and all blobs are uniquely identified by their OID, a 40-character string similar to the SHA-1 used by Git. The OIDs for pack files and blobs are used to spread the files into subdirectories and avoid having thousands of files directly under .nt/objects
. For example, the pack file afe988e5f40e4d1181a86f522e3c1f2e6f0241e3
and the blob 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4
will be stored like this:
.nt└── objects ├── 6e │ └── 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4.blob └── af └── afe988e5f40e4d1181a86f522e3c1f2e6f0241e3.pack
.nt/objects/{xx}/{packfile-sha1}
Pack files are YAML objects.
Ex:
oid: 4a03d1ab # A unique OID for this pack filefile_relative_path: go.md # Relative path of the source filefile_mtime: 2023-01-01T12:30:00Z # Mtime of the source filefile_size: 134 # Size of the source filectime: 2023-01-01T12:00:00 # The creation timeobjects: # The list of all objects added/modified/deleted (a new pack file cannot contain more than a predefined number of objects) - oid: d19a2bba # The object OID kind: file # The object kind ctime: 2023-01-01T12:00:00 # The creation time of the object desc: file "hello.md" [d19a2bba] # A human-readable description of the object data: <value> # A base64-encoded representation of the object - oid: 6ee8a962 kind: note Ctime: 2023-01-01T12:00:00 desc: 'note "Note: Hello" [6ee8a962]' data: <value>
Each object is self-containing through the data
attribute. The value is compressed using zlib and encoded in Base 64. You can easily retrieve the uncompressed content:
# On MacOS$ brew install qpdf$ echo "<value>" | base64 -d | zlib-flate -uncompressoid: 6ee8a962file_oid: d19a2bbatype: Noterelative_path: hello.mdwikilink: 'hello#Note: Hello'content: Coucoucontent_hash: b70f7d0e2acef2e0fa1c6f117e3c11e0d7082232...
The main motivation behind pack files is to limit the number of files on disk (and the number of files to transfer when using a remote repository).
.nt/index
The index
file serves multiple purposes. It contains the staging area (= the list of pack files to include in the next commit) and keeps a list of all pack files to quickly locate an object or a blob.
Ex:
committed_at: 2023-01-01T12:00:00 # The last committed timestampentries: - relative_path: "go.md" # Relative path of the source file packfile_oid: a3455b # Committed pack file containing # the last known version of this source file mtime: 2022-12-11T02:14:00 # Mtime and size of the source file... size: 123 # ... when the file was committed - relative_path: "python.md" packfile_oid: 00000 # Never committed mtime: 0001-01-01T00:00:00 size: 0 # For staged entries
staged: true # True when a file has been staged staged_packfile_oid: 837291 # Pack file containing the staged version staged_mtime: 2023-01-01T08:42:00 # Mtime and size of the source file... staged_size: 2349 # ... when the file was staged staged_tombstone: 0001-01-01T00:00:00 # Tombstone for staged deleted files ...objects: - oid: 983712 kind: note packfile_oid: a3455b ...blobs: - oid: 98ab19 mime: audio/mpeg packfile_oid: 837291 ...
.nt/database.db
In addition to raw files, The NoteWriter also comprises a SQLite database (populated using the same information as present in object files, included staged ones). This database is used to speed up commands but also to benefit from the full-text search support when using the desktop application.
Example
-
Setup a new repository
Terminal window $ mkdir notes$ cd notes$ nt initInspect the database:
Terminal window .├── .nt│ ├── .gitignore│ └── config└── .ntignoreMost files are still missing and will be populated only after adding files.
-
Add a new note
Terminal window $ nt init$ echo "# Note: Hello\n\nCoucou" > hello.md$ nt add hello.md && nt commit[bf712c5de01642338ce2d16a37daabeb37daabeb]2 objects changes, 2 insertion(s)create file "hello.md" [d19a2bba42d44d8a82b18b2edcd4320612a3dfbc]create note "Note: Hello" [6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4]Inspect the database:
Terminal window $ tree -a.├── .nt│ ├── .gitignore│ ├── config│ ├── database.db│ ├── index│ └── objects│ ├── 4a│ │ └── 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6.pack│ └── 67│ └── 67937d98e9cba4df937d41348e6d4eec5d11546c.blob├── .ntignore└── hello.mdDatabase files have now been created. We have new objects under
objects
representing the unique pack file (4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6
) containing two objects:Terminal window $ nt cat-fileoid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6file_relative_path: hello.mdfile_mtime: 2023-01-01T11:30:00Zfile_size: 134ctime: 2023-01-01T12:00:00objects:- oid: d19a2bba42d44d8a82b18b2edcd4320612a3dfbckind: filemtime: 2023-01-01T12:00:00desc: file "hello.md" [d19a2bba42d44d8a82b18b2edcd4320612a3dfbc]data: aBc...=- oid: 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4kind: notemtime: 2023-01-01T12:00:00desc: 'note "Note: Hello" [6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4]'data: eFg...=And our index has been updated with new entries that are no longer staged:
Terminal window $ cat .nt/indexcommitted_at: 2023-01-01T12:00:00entries:- relative_path: hello.mdpackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6mtime: 2023-01-01T12:00:00size: 1234objects:- oid: d19a2bba42d44d8a82b18b2edcd4320612a3dfbckind: filepackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6- oid: 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4kind: notepackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6blobs:- oid: 67937d98e9cba4df937d41348e6d4eec5d11546cmime: text/markdownpackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6 -
Edit the note to reference a new media
Terminal window $ cp ~/me.png .$ echo "\n"'' >> hello.md$ nt addCheck that the staging area is not empty:
Terminal window $ cat .nt/indexcommitted_at: 2023-01-01T12:00:00entries:- relative_path: hello.mdpackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6mtime: 2023-01-01T12:00:00size: 1234staged: truestaged_packfile_oid: 0733657a2ca447beadfbce2832a26035deb1634estaged_mtime: 2023-01-02T12:00:00staged_size: 1245- relative_path: me.pngpackfile_oid: 0000000000000000000000000000000000000000mtime: 0001-01-01T00:00:00size: 0staged: truestaged_packfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1staged_mtime: 2023-01-02T12:00:00staged_size: 1245objects:- oid: d19a2bba42d44d8a82b18b2edcd4320612a3dfbckind: filepackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4kind: notepackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: 634a860212ee4ad392dd47a375aa88431a494f1ckind: mediapackfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1blobs:- oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6mime: text/markdownpackfile_oid: 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6- oid: 0733657a2ca447beadfbce2832a26035deb1634emime: text/markdownpackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: a977d1c4ed76444e9ff05c37f2ce2c3be0fa7b55mime: image/avifpackfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1 -
Commit changes
Terminal window $ nt commit[c34575fba9884d62b5512e2c5fbc274c5fbc274c]3 objects changes, 1 insertion(s), 2 modification(s)create media me.png [3837a10fbc3a47c7961896febf64463b4a006c79]modify file "hello.md" [d19a2bba42d44d8a82b18b2edcd4320612a3dfbc]modify note "Note: Hello" [6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4]$ tree -a.├── .nt│ ├── .gitignore│ ├── config│ ├── database.db│ ├── index│ └── objects│ ├── 06│ │ └── 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1.pack│ ├── 07│ │ └── 0733657a2ca447beadfbce2832a26035deb1634e.pack│ └── 4a│ └── 4a03d1ab3dbe4c5d9efacd0e05e187179c5415c6.pack├── .ntignore├── hello.md└── me.pngThe commit has been recorded:
Terminal window $ cat .nt/objects/indexcommitted_at: 2023-01-02T12:00:00entries:- relative_path: hello.mdpackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634emtime: 2023-01-02T12:00:00size: 1245- relative_path: me.pngpackfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1mtime: 2023-01-02T12:00:00size: 1245objects:- oid: d19a2bba42d44d8a82b18b2edcd4320612a3dfbckind: filepackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: 6ee8a9620d3f4d3f9fbd159744ef85b83400b0d4kind: notepackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: 634a860212ee4ad392dd47a375aa88431a494f1ckind: mediapackfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1blobs:- oid: 0733657a2ca447beadfbce2832a26035deb1634emime: text/markdownpackfile_oid: 0733657a2ca447beadfbce2832a26035deb1634e- oid: a977d1c4ed76444e9ff05c37f2ce2c3be0fa7b55mime: image/avifpackfile_oid: 06400bd13f8a43c8a5b5f3db41f60dac7bfa78f1
That’s all. You have seen the various files in action.