Skip to content

neutralinsomniac/exocortex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

241 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

exocortex

A note-taking/todo/information-storage system. There are two clients:

  • exo — a terminal UI for desktop/laptop, written in Go with bubbletea
  • tdeck-exo — a firmware port for the LilyGo T-Deck Plus, a portable device with a 320×240 display and a physical QWERTY keyboard

Concepts

exocortex tries to be as friction-free as possible when it comes to capturing information without worrying about organization upfront.

On startup, exo reopens the last tag you were on, or the inbox tag if none exists yet.

Tags are the top-level organizational unit. They are created explicitly via the new tag command, or on-the-fly by referencing them in row text using [[double brackets]]. Tags are automatically deleted when they have no rows and no rows reference them.

Rows are bullets under a tag. When a row references another tag (e.g. [[todo]] take out the trash), exocortex links that row bidirectionally — viewing the todo tag will show a reference back to the originating tag with the full row text visible and editable.

Installation

go install github.com/neutralinsomniac/exocortex/cmd/exo@latest

Or with Nix:

nix profile add github:neutralinsomniac/exocortex#exo

To install into a NixOS configuration flake, add it as an input and pass it through to your NixOS module:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
    exocortex.url = "github:neutralinsomniac/exocortex";
    exocortex.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = { nixpkgs, exocortex, ... } @ inputs: {
    nixosConfigurations.myhostname = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      specialArgs = { inherit inputs; };
      modules = [ ./configuration.nix ];
    };
  };
}

Then in configuration.nix:

{ pkgs, inputs, ... }:
{
  environment.systemPackages = [
    inputs.exocortex.packages.${pkgs.system}.exo
    inputs.exocortex.packages.${pkgs.system}.exo-server
  ];
}

Usage

Run exo from anywhere. All data is stored in a single SQLite database at $XDG_DATA_HOME/exocortex/exocortex.db (defaulting to ~/.local/share/exocortex/exocortex.db).

Press ? inside the app to open the help screen.

Keybindings

Tags

Key Action
i Go to inbox tag
n New tag
r Rename current tag
t Tag selector (type to filter)
/ Fuzzy search all rows
\ Toggle show/hide done rows
ctrl-t Go back in tag history
19 Jump to numbered tag reference

Rows

Key Action
g / G Jump to first / last row
j / k Move cursor down / up
enter Follow tag link in selected row
o / O Add row below / above cursor
e Edit selected row
N Add/edit note on selected row
d Cut selected row (or all marked)
D Mark done / un-done (or all marked)
y Yank (copy) selected row
p / P Paste below / above cursor
J / K Move row down / up within its priority group
space Toggle row in/out of multi-selection
; Clear multi-selection
! @ # $ % Set priority 1–5 (repeat to clear)
) Clear priority
u / U Undo / redo
S Sync with server
? Help
q Quit

T-Deck Plus firmware (tdeck-exo)

The tdeck-exo/ directory contains a PlatformIO project that builds firmware for the LilyGo T-Deck Plus. It shares the same sync protocol as exo, so a T-Deck and a desktop instance stay in sync through the same server.

Building

Open tdeck-exo/ in PlatformIO and flash the lilygo_tdeck_plus environment.

Configuration

Edit tdeck-exo/src/config.h before flashing:

Setting Description
WIFI_SSID / WIFI_PASSWORD Network to connect to for sync and NTP
SYNC_URL Server URL — https:// for TLS, http:// for AES-256-GCM payload encryption
SYNC_TOKEN Shared secret matching the server's -token flag
TLS_INSECURE Set false to verify the server's TLS certificate
NTP_SERVER NTP server for time sync (default: pool.ntp.org)
TZ_OFFSET_HOURS UTC offset for your timezone (e.g. -5 for EST)
BOARD_BAT_ADC GPIO pin for battery voltage ADC (default: 4)
SLEEP_TIMEOUT_MS Deep sleep after this many ms of inactivity (default: 30000; set to 0 to disable)
STATIC_IP (optional) Static IP address — bypasses DHCP, avoids repeated DHCP requests on each wake
STATIC_GATEWAY (optional) Gateway address (required if STATIC_IP is set)
STATIC_SUBNET (optional) Subnet mask (required if STATIC_IP is set)
STATIC_DNS (optional) DNS server (required if STATIC_IP is set)

Keybindings

Tag list

Key Action
j / k or trackball Move cursor
Enter Open tag
i Go to inbox
n New tag
Type Filter tags
Backspace Clear filter / return to row list

Row list

Key Action
j / k or trackball Move cursor
o New row
e Edit selected row
d Cut selected row (copied to clipboard)
y Yank (copy) selected row
p / P Paste clipboard row after / before cursor
D Toggle done
15 Set priority (press same key again to clear)
0 Clear priority
J / K Move row up / down within its priority group
h Toggle show/hide done rows
Enter / l Follow [[tag]] link in selected row
b Go back in tag history
t Tag list
i Go to inbox
s Sync with server
Backspace Sleep immediately

Row edit

Key Action
Trackball left / right Move cursor within text
Enter Save
Escape Cancel

Sync

exo-server is a companion binary that lets you keep a hosted exocortex database and sync to it from multiple machines. Sync is bidirectional with last-write-wins conflict resolution per row.

Running the server

go install github.com/neutralinsomniac/exocortex/cmd/exo-server@latest
exo-server -db /path/to/exocortex.db -token <secret> -cert /path/to/cert.pem -key /path/to/key.pem
Flag Default Description
-db (required) Path to the SQLite database file
-token (required) Shared secret used to authenticate clients
-cert TLS certificate file (PEM). Must be paired with -key
-key TLS private key file (PEM). Must be paired with -cert
-addr :8765 Address to listen on

Encryption modes

With -cert and -key (TLS): The server listens with HTTPS. All traffic is encrypted via TLS. The sync payload is transmitted as plain JSON over the encrypted connection. Configure the client with an https:// URL.

Without -cert and -key (symmetric encryption): The server listens with plain HTTP, but the sync payload itself is encrypted end-to-end using AES-256-GCM. The encryption key is derived from the shared -token via SHA-256. Both request and response bodies are opaque binary blobs (application/octet-stream). No Authorization header is sent — a successful decryption already proves knowledge of the token, so the token never appears in plaintext on the wire. Configure the client with an http:// URL.

The client automatically selects the encryption mode based on the sync_url setting: https:// URLs use TLS (no payload encryption), and http:// URLs use AES-256-GCM payload encryption.

Configuring the client

Set the server URL and token in the exocortex settings table using any SQLite client:

sqlite3 ~/.local/share/exocortex/exocortex.db \
  "INSERT OR REPLACE INTO settings VALUES ('sync_url', 'https://yourserver:8765');
   INSERT OR REPLACE INTO settings VALUES ('sync_token', 'yoursecret');"

Once configured, press S inside exo to sync. Status is shown in the bottom bar.

About

Note taking/information storage + retrieval system inspired by https://roamresearch.com

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors