touchlog

A knowledge graph note-taking system built with Go. touchlog provides a powerful CLI for managing structured notes with automatic link resolution, graph queries, and real-time indexing.
Features
- Vault-based organization: Organize notes in type-specific directories with structured frontmatter
- Automatic indexing: SQLite-based index with automatic link resolution and incremental updates
- Graph queries: Find backlinks, neighbors, and paths between notes using graph traversal
- Graph visualization: Export knowledge graphs to Graphviz DOT format
- Real-time updates: Daemon mode with filesystem watching for automatic index updates
- Structured notes: YAML frontmatter with validation and diagnostics
- Wiki-style links: Support for
[[note:key]], [[key]], and edge-type annotations
- Deterministic exports: Stable, diffable JSON and DOT exports
Installation
Prerequisites
Build from Source
git clone https://github.com/sv4u/touchlog.git
cd touchlog
go build ./cmd/touchlog
The binary will be created in the current directory as touchlog.
Install via Go
go install github.com/sv4u/touchlog/cmd/touchlog@latest
Quick Start
-
Initialize a vault:
touchlog init
This creates a .touchlog directory with a default configuration file.
-
Create a note:
touchlog new
Follow the interactive prompts to create a new note with frontmatter.
-
Build the index:
touchlog index rebuild
This scans all .Rmd files in your vault and builds the index.
-
Query your notes:
touchlog query search --type note
touchlog query backlinks --target note:my-note
touchlog query neighbors --root note:my-note --max-depth 2
touchlog query paths --source note:start --destination note:end --max-depth 5
-
Export the graph:
touchlog graph export dot --out graph.dot
dot -Tpng graph.dot -o graph.png
Vault Structure
A touchlog vault is a directory containing:
vault/
├── .touchlog/
│ ├── config.yaml # Vault configuration
│ └── index.db # SQLite index (auto-generated)
├── note/ # Type-specific directories
│ ├── my-note.Rmd
│ └── another-note.Rmd
├── article/
│ └── my-article.Rmd
└── ...
Note Files
Notes are stored as .Rmd files with YAML frontmatter:
---
id: note-001
type: note
key: my-note
title: My Note
state: draft
tags: [important, todo]
created: 2024-01-01T00:00:00Z
updated: 2024-01-01T00:00:00Z
---
# My Note
This note links to [[note:another-note]] and [[article:my-article]].
You can also use unqualified links: [[another-note]] if the key is unique.
Configuration
The vault configuration file (.touchlog/config.yaml) defines:
- Types: Note types with validation rules (key patterns, default states)
- Tags: Tag configuration and preferred tags
- Edges: Edge type definitions for relationships
- Templates: Template configuration (future)
Example configuration:
version: 1
types:
note:
description: A note
default_state: draft
key_pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
key_max_len: 64
article:
description: An article
default_state: draft
key_pattern: ^[a-z0-9]+(-[a-z0-9]+)*$
key_max_len: 64
tags:
preferred: [important, todo, reference]
edges:
related-to:
description: General relationship
references:
description: Reference relationship
templates:
root: templates
Commands
Vault Management
touchlog init - Initialize a new vault in the current directory
touchlog new - Create a new note interactively
Index Management
touchlog index rebuild - Rebuild the entire index from scratch
touchlog index export --out <file> - Export the index to JSON
Querying
-
touchlog query search [options] - Search notes with filters
--type <types> - Filter by types (comma-separated)
--state <states> - Filter by states (comma-separated)
--tag <tags> - Filter by tags (comma-separated)
--match-any-tag - Match any tag (default: match all)
--limit <n> - Limit results
--format table|json - Output format
-
touchlog query backlinks --target <node> [options] - Find backlinks to a node
--direction in|out|both - Link direction (default: in)
--edge-type <types> - Filter by edge types
--format table|json - Output format
-
touchlog query neighbors --root <node> --max-depth <n> [options] - Find neighbors
--direction in|out|both - Link direction (default: both)
--edge-type <types> - Filter by edge types
--format table|json - Output format
-
touchlog query paths --source <node> --destination <node> [--destination <node> ...] --max-depth <n> [options] - Find paths
--max-paths <n> - Maximum paths per destination (default: 10)
--direction in|out|both - Link direction (default: both)
--edge-type <types> - Filter by edge types
--format table|json - Output format
Graph Operations
touchlog graph export dot --out <file> [options] - Export graph to DOT format
--root <node> [--root <node> ...] - Root nodes (can specify multiple)
--type <types> - Filter by types
--state <states> - Filter by states
--tag <tags> - Filter by tags
--edge-type <types> - Filter by edge types
--depth <n> - Maximum depth (default: 10)
--force - Overwrite existing file
Daemon
touchlog daemon start - Start the daemon for real-time indexing
touchlog daemon stop - Stop the daemon
touchlog daemon status - Check daemon status
Architecture
touchlog follows a contracts-first architecture with clear separation of concerns:
Core Components
- Models (
internal/model/): Canonical data structures (Note, Frontmatter, RawLink, etc.)
- Config (
internal/config/): Configuration loading and validation
- Note Parser (
internal/note/): Frontmatter parsing and wiki-link extraction
- Store (
internal/store/): SQLite persistence layer with migrations
- Index (
internal/index/): Full-scan indexing with atomic rebuilds
- Query (
internal/query/): Search and graph query execution
- Graph (
internal/graph/): Graph loading and export
- Daemon (
internal/daemon/): IPC server and lifecycle management
- Watch (
internal/watch/): Filesystem watching and incremental indexing
Design Principles
- Contracts First: Models and schemas defined before behavior
- Derived State Only: Index and exports are always rebuildable
- Determinism: All outputs are stable and diffable
- Explicit Errors: Never silently fail or coerce invalid input
- Diagnostics: Validation produces diagnostics, not hard failures
Indexing
The index is built in two passes:
- Pass 1: Parse all notes, build
(type, key) -> id map
- Pass 2: Resolve all links using the type/key map
The index is stored in SQLite with the following schema:
meta: Metadata (schema version, etc.)
nodes: Note nodes with frontmatter
edges: Links between notes (with resolved to_id)
tags: Tags associated with notes
diagnostics: Parse errors and warnings
Graph Queries
All graph queries (backlinks, neighbors, paths) execute in-memory after loading the relevant subgraph from SQLite. This approach:
- Avoids complex recursive SQL
- Simplifies BFS correctness and determinism
- Aligns with future TUI graph viewer needs
All traversals maintain visited sets for cycle detection and guarantee termination.
Examples
Creating and Linking Notes
# Create a note
touchlog new
# Follow prompts to create note with key "introduction"
# Create another note that links to it
touchlog new
# Create note with key "getting-started" and add link: [[note:introduction]]
# Rebuild index
touchlog index rebuild
# Find backlinks to introduction
touchlog query backlinks --target note:introduction
# Find all neighbors within 2 hops
touchlog query neighbors --root note:my-note --max-depth 2
# Find paths between notes
touchlog query paths --source note:start --destination note:end --max-depth 5
# Export graph for visualization
touchlog graph export dot --out graph.dot
dot -Tsvg graph.dot -o graph.svg
Using the Daemon
# Start daemon for real-time indexing
touchlog daemon start
# Create/edit notes - index updates automatically
# Query works immediately
touchlog query search --type note
# Stop daemon
touchlog daemon stop
Development
Project Structure
touchlog/
├── cmd/
│ └── touchlog/
│ └── main.go # CLI entry point
├── internal/
│ ├── model/ # Core data models
│ ├── config/ # Configuration loading
│ ├── note/ # Note parsing
│ ├── store/ # SQLite persistence
│ ├── index/ # Indexing logic
│ ├── query/ # Query execution
│ ├── graph/ # Graph operations
│ ├── daemon/ # Daemon and IPC
│ ├── watch/ # Filesystem watching
│ └── cli/ # CLI command definitions
├── testdata/
│ ├── vaults/ # Test vault fixtures
│ └── golden/ # Golden test fixtures
├── go.mod
└── README.md
Running Tests
# Run all tests
go test ./...
# Run tests with coverage
go test ./... -cover
# Run specific package tests
go test ./internal/query -v
Building
# Build binary
go build ./cmd/touchlog
# Build for specific platform
GOOS=linux GOARCH=amd64 go build ./cmd/touchlog
Contributing
Commit Message Guidelines
This project follows the Conventional Commits specification.
Commit Types:
feat: A new feature
fix: A bug fix
docs: Documentation only changes
test: Adding or updating tests
refactor: Code refactoring without changing functionality
perf: Performance improvements
ci: Changes to CI/CD configuration
chore: Other changes that don't modify src or test files
Examples:
feat(query): add backlinks command
fix(index): resolve link resolution bug
docs(readme): update architecture section
test(query): add edge case tests for cycles
License
See the LICENSE file for details.