
Obsidian I/O Solutions
Project Links
The Problem
When I moved away from Emacs and org-mode a few years ago, I was scared. Not of losing features, but of repeating the same mistake: building my entire workflow on top of a single-implementation system with no specification.
Org-mode is powerful, but it's impossible to interact with headlessly or integrate with other tools. Obsidian offered a better foundation (markdown, plugins, a less clunky API), but the risk was the same: if everything lives inside Obsidian, I'm locked in.
So I spent some time figuring out how to get data in and out of Obsidian without touching its UI.
The Architecture
The system has three layers: data ingestion, query materialization, and consumption.
(Tridactyl)"] mobile["Mobile
(Wallabag)"] nvimNotes["Neovim
note-taking"] end subgraph External["External Tools"] capturemd["capturemd"] end subgraph Filesystem["Filesystem"] markdown["Markdown Files"] json["JSON Cache"] end subgraph Obsidian["Obsidian Runtime"] templater["Templater"] dataview["Dataview"] deeplink["Deep Links"] end subgraph Consumers["Consumers"] html["HTML Dashboard"] telescope["Telescope Pickers"] dvtui["dv-tui"] nvimBuf["Neovim Buffers"] end subgraph Triggers["Triggers"] hyprland["Hyprland Keybind"] cron["Cron Jobs"] end browser --> capturemd mobile --> capturemd nvimNotes --> markdown capturemd <--> markdown markdown <--> Obsidian hyprland --> deeplink cron --> deeplink deeplink --> templater templater --> dataview dataview --> json json --> html json --> telescope json --> dvtui json --> nvimBuf html -.-> deeplink dvtui -.-> nvimNotes
Data In: capturemd
capturemd handles ingestion. It captures URLs from urls (via browers or cli) or mobile (via Wallabag), and converts them into structured markdown notes with frontmatter.
The notes are bare at first (just a URL and platform ID), then capturemd parse contacts the relevant APIs (YouTube, Reddit, RSS feeds) and populates the metadata.
Query Engine: Dataview + Templater
Dataview queries run inside Obsidian, but I don't render them within some dataview/dataviewjs code block in markdown. Instead, I serialize the results to JSON files on disk. This lets any external tool consume the data without needing Obsidian.
The queries are triggered remotely via deep links:
obsidian "obsidian://advanced-uri?eval=$(echo 'tp.user.someFunction(tp)' | jq -sRr @uri)"
where someFunction.js is a Templater module - as seen previously
A single keybind in Hyprland (Win+Q) re-runs all queries and regenerates the JSON cache.
Data Out: Multiple Frontends
The JSON files feed into multiple consumers:
- HTML Dashboard: Static site generated from the JSONs, with
obsidian://links to send actions back - Telescope Pickers: Neovim fuzzy finders that read the JSON directly
- dv-tui: ncurses TUI that runs in tmux popups
- Fake Buffers: Neovim buffers where deleting a line removes the entry from the JSON
All of these can open files in Neovim (via socket) or trigger actions back in Obsidian (via deep links).
Where I Am Now
Obsidian sits on a dedicated Hyprland workspace. I rarely open it anymore except for Excalidraw drawings or screen sharing on calls - markdown in a terminal raises questions (although with the rise of terminal based llm clients and absolutely everything-as-markdown, maybe not as much in the future).
The actual note-taking and task management happens through the TUI and Telescope pickers. Queries refresh with a keybind or on an timer.
I easily fly between projects and my notes with my project selector.
I have a query listing all my projects, then the project selector script is bound to tmux-leader+P which brings as a tmux popup window (another one), then upon pressing:
enter: it will look at the "workspace" field of the frontmatter and run the tmux-sessionizer on this path (https://github.com/ThePrimeagen/tmux-sessionizer). if the tmux-session exist it will switch to it and if it does not it will create itctrl+o: open the note associated with the project in my note-taking instance of nvim.
Between, the ability to easily link project management note, related codebases, alternate/list files in nvim, alternate/list sessions in tmux, this allows for very effecient never-pausing context switching between documentation, notes and code across worktrees.
The separation of "runtime" (Obsidian) and "interface" (everything else) is mostly complete.
What's Still Wrong
The system works, but it's fragile:
- Too many moving parts. Lua scripts, Python TUIs, JavaScript modules, JSON files in expected locations. It's not portable.
- Obsidian dependency. The queries still run inside Obsidian. If I want to fully decouple, I need to reimplement Dataview's query engine.
- No live refresh. I have to manually trigger query re-runs. There's no watch mode.
What's Next
The long-term goal is to replace the Obsidian/Dataview runtime with a headless daemon. Same markdown files, same query syntax, but no Electron app required.
The socket-based plugin prototype already works for remote execution (more on this later).