File Watching
The daemon can automatically create memories from files in watched directories. When a file matching your patterns is created or modified, the daemon reads its contents and creates a memory in Trix.
Enabling File Watching
File watching is disabled by default for security. Enable it in your configuration:
# ~/.trix/daemon.yaml
watcher:
enabled: true
directories:
- ~/Documents/notes
- ~/Projects/work/docs
- /shared/team-wiki
patterns:
- "*.md"
- "*.txt"
- "*.org"
ignore_hours: 24
debounce_ms: 500Pattern Matching
File patterns use standard glob syntax:
| Pattern | Matches |
|---|---|
*.md | All markdown files in watched directories |
*.txt | All text files |
README* | Files starting with README |
docs/*.md | Markdown files in docs subdirectory |
**/*.md | Markdown files in any subdirectory (recursive) |
*.{md,txt} | Markdown or text files |
Watcher Behavior
- Creation - New files matching patterns are ingested immediately
- Modification - Updated files trigger re-ingestion (creates new memory)
- Debouncing - Rapid changes are debounced to avoid duplicate ingestion
- Age Filter - Files older than
ignore_hoursare skipped on startup
Memory Metadata
Memories created from files include useful metadata:
{
"id": "mem_abc123",
"content": "Contents of the file...",
"metadata": {
"source": "file",
"filepath": "/home/user/notes/meeting.md",
"filename": "meeting.md",
"directory": "/home/user/notes",
"size_bytes": 1234,
"ingested_at": "2024-01-15T10:30:00Z",
"modified_at": "2024-01-15T10:25:00Z"
},
"tags": ["file"]
}Supported File Types
- Text files -
.txt,.md,.org,.rst - Code files -
.py,.js,.go, etc. - Config files -
.yaml,.json,.toml - Documents -
.pdf(text extraction)
Security Considerations:
- Only watch directories you explicitly trust
- Be cautious with broad patterns like
* - Avoid watching directories with sensitive files (credentials, keys)
- The daemon runs with your user permissions
Troubleshooting File Watching
# Check watcher status
trixd status
# View watcher events in logs
trixd logs -f | grep watcher
# Run with debug logging to see all events
trixd run -vvLinux inotify limits: If watching many directories, you may need to increase the inotify limit:
# Temporary (until reboot)
sudo sysctl fs.inotify.max_user_watches=524288
# Permanent
echo "fs.inotify.max_user_watches=524288" | sudo tee -a /etc/sysctl.conf.trixignore Files
The .trixignore file allows you to exclude files and directories from
being watched and ingested by the daemon. It uses the same syntax as .gitignore,
making it familiar to developers.
Enabling .trixignore Support
To use .trixignore files, enable them in your daemon configuration:
# ~/.trix/daemon.yaml
watcher:
enabled: true
directories:
- ~/Documents/notes
- ~/Projects
patterns:
- "*.md"
- "*.txt"
# Enable .trixignore file discovery
check_ignore_files: true
# Optional: customize the ignore file name (default: .trixignore)
ignore_file_name: ".trixignore"Creating a .trixignore File
Place a .trixignore file in any watched directory. The rules apply to
that directory and all its subdirectories.
# ~/Projects/.trixignore
# Build outputs
build/
dist/
out/
*.o
*.pyc
*.class
# Dependencies
node_modules/
vendor/
venv/
__pycache__/
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*~
.DS_Store
# Version control
.git/
.svn/
.hg/
# Temporary files
*.tmp
*.temp
*.bak
*.log
# Secrets and credentials (never ingest these!)
.env
.env.local
*.pem
*.key
credentials.json
# Large binary files
*.zip
*.tar.gz
*.mp4
*.mov
# Re-include specific files (negation)
!README.md
!docs/*.md
!.vscode/settings.jsonPattern Syntax Reference
The .trixignore file supports full gitignore syntax:
| Pattern | Description | Example Matches |
|---|---|---|
*.log | Wildcard - matches any .log file | app.log, error.log |
build/ | Directory - matches directories only | build/, src/build/ |
**/node_modules/** | Double-star - matches at any depth | node_modules/, a/b/node_modules/pkg/ |
/config.yaml | Root-anchored - only in root directory | config.yaml (not sub/config.yaml) |
!important.md | Negation - re-includes previously ignored | Include important.md even if *.md is ignored |
# comment | Comment - ignored line | - |
\#file | Escaped - matches literal #file | #file |
doc?.txt | Single char wildcard | doc1.txt, docA.txt |
*.[oa] | Character class | file.o, file.a |
Config vs .trixignore
You can use both global ignore_patterns in config and per-directory .trixignore files:
| Feature | Config ignore_patterns | .trixignore File |
|---|---|---|
| Scope | All watched directories | Directory and subdirectories |
| Location | ~/.trix/daemon.yaml | In watched directories |
| Version Control | Usually not committed | Can be committed to repo |
| Use Case | System-wide excludes | Project-specific excludes |
| Priority | Applied first | Applied after, can override |
Best Practice: Use config ignore_patterns for system-wide excludes like node_modules and .git. Use .trixignore files for project-specific rules that should be shared with your team.
Common .trixignore Examples
Node.js Project
# Node.js .trixignore
node_modules/
dist/
build/
coverage/
.nyc_output/
*.log
npm-debug.log*
.env
.env.localPython Project
# Python .trixignore
__pycache__/
*.py[cod]
*$py.class
venv/
.venv/
env/
.eggs/
*.egg-info/
dist/
build/
.tox/
.pytest_cache/
.mypy_cache/Go Project
# Go .trixignore
vendor/
bin/
*.exe
*.dll
*.so
*.dylib
*.test
*.out
go.sumDocumentation Project
# Docs .trixignore
_site/
.jekyll-cache/
.sass-cache/
node_modules/
# Include all markdown except drafts
!*.md
drafts/Security Best Practices
Always ignore sensitive files:
.env- Environment variables with secrets*.pem,*.key- Private keys and certificatescredentials.json- API credentials*.sqlite,*.db- Database filesconfig/secrets.*- Secret configuration files
Add these to your global config ignore_patterns for maximum safety.
Debugging Ignore Patterns
To see which files are being ignored, run the daemon with debug logging:
# See ignore pattern matching in real-time
trixd run -vv 2>&1 | grep -E "(ignored|pattern)"
# Check daemon status including ignore stats
trixd status --json | jq '.watcher'
# Check daemon status including ignore stats
trixd status --json | jq '.watcher.ignored_count'IPC Integration
The daemon exposes a Unix socket for local inter-process communication. This allows other tools on your machine to interact with Trix through the daemon without making direct API calls.
Socket Location
By default, the socket is at ~/.trix/daemon.sock. The Trix CLI automatically
uses this socket when the daemon is running for faster operations and offline support.
Protocol
The IPC uses JSON-RPC 2.0 over the Unix socket:
// Request format
{
"jsonrpc": "2.0",
"id": 1,
"method": "method_name",
"params": { ... }
}
// Success response
{
"jsonrpc": "2.0",
"id": 1,
"result": { ... }
}
// Error response
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32600,
"message": "Invalid request"
}
}Available Methods
| Method | Parameters | Description |
|---|---|---|
ping | none | Check if daemon is responsive |
status | none | Get detailed daemon status |
sync | {"force": bool} | Trigger immediate sync with API |
search | {"query": string, "limit": int} | Search cached memories locally |
get | {"id": string} | Get a specific memory from cache |
invalidate | {"id": string} or {"all": bool} | Clear specific cache entries |
config | none | Get current configuration |
Method Examples
ping
// Request
{"jsonrpc": "2.0", "id": 1, "method": "ping", "params": {}}
// Response
{"jsonrpc": "2.0", "id": 1, "result": {"pong": true, "timestamp": "2024-01-15T10:30:00Z"}}status
// Request
{"jsonrpc": "2.0", "id": 1, "method": "status", "params": {}}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"status": "running",
"uptime_seconds": 3600,
"cache_count": 1234,
"cache_size_bytes": 52428800,
"api_connected": true,
"last_sync": "2024-01-15T10:29:30Z",
"watchers_active": 2
}
}search
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "search",
"params": {
"query": "user preferences",
"limit": 5
}
}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"memories": [
{"id": "mem_abc123", "content": "User prefers dark mode...", "score": 0.92},
{"id": "mem_def456", "content": "Preferred language is Python...", "score": 0.85}
],
"from_cache": true
}
}sync
// Request - trigger immediate sync
{"jsonrpc": "2.0", "id": 1, "method": "sync", "params": {"force": true}}
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"success": true,
"memories_synced": 42,
"duration_ms": 1234
}
}Go Client Example
package main
import (
"encoding/json"
"fmt"
"net"
"os"
)
type Request struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params map[string]interface{} `json:"params"`
}
type Response struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Result json.RawMessage `json:"result"`
Error *struct {
Code int `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
func main() {
sockPath := os.ExpandEnv("$HOME/.trix/daemon.sock")
conn, err := net.Dial("unix", sockPath)
if err != nil {
panic(err)
}
defer conn.Close()
// Send status request
req := Request{
JSONRPC: "2.0",
ID: 1,
Method: "status",
Params: map[string]interface{}{},
}
json.NewEncoder(conn).Encode(req)
// Read response
var resp Response
json.NewDecoder(conn).Decode(&resp)
if resp.Error != nil {
fmt.Printf("Error: %s\n", resp.Error.Message)
return
}
fmt.Printf("Status: %s\n", string(resp.Result))
}Python Client Example
import socket
import json
import os
class TrixDaemonClient:
def __init__(self, sock_path=None):
self.sock_path = sock_path or os.path.expanduser("~/.trix/daemon.sock")
self._id = 0
def _request(self, method: str, params: dict = None) -> dict:
self._id += 1
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self.sock_path)
request = {
"jsonrpc": "2.0",
"id": self._id,
"method": method,
"params": params or {}
}
sock.send(json.dumps(request).encode() + b"\n")
response = json.loads(sock.recv(65536).decode())
sock.close()
if "error" in response:
raise Exception(response["error"]["message"])
return response.get("result")
def ping(self) -> bool:
result = self._request("ping")
return result.get("pong", False)
def status(self) -> dict:
return self._request("status")
def search(self, query: str, limit: int = 10) -> list:
result = self._request("search", {"query": query, "limit": limit})
return result.get("memories", [])
def sync(self, force: bool = False) -> dict:
return self._request("sync", {"force": force})
# Usage
client = TrixDaemonClient()
if client.ping():
status = client.status()
print(f"Cache contains {status['cache_count']} memories")
memories = client.search("user preferences", limit=5)
for mem in memories:
print(f"- {mem['id']}: {mem['content'][:50]}...")Node.js Client Example
const net = require('net');
const os = require('os');
const path = require('path');
class TrixDaemonClient {
constructor(sockPath) {
this.sockPath = sockPath || path.join(os.homedir(), '.trix', 'daemon.sock');
this.id = 0;
}
async request(method, params = {}) {
return new Promise((resolve, reject) => {
const client = net.createConnection(this.sockPath);
const request = {
jsonrpc: '2.0',
id: ++this.id,
method,
params
};
client.write(JSON.stringify(request) + '\n');
let data = '';
client.on('data', chunk => data += chunk);
client.on('end', () => {
const response = JSON.parse(data);
if (response.error) {
reject(new Error(response.error.message));
} else {
resolve(response.result);
}
});
client.on('error', reject);
});
}
async ping() {
const result = await this.request('ping');
return result.pong;
}
async status() {
return this.request('status');
}
async search(query, limit = 10) {
const result = await this.request('search', { query, limit });
return result.memories;
}
}
// Usage
const client = new TrixDaemonClient();
(async () => {
if (await client.ping()) {
const status = await client.status();
console.log(`Cache contains ${status.cache_count} memories`);
const memories = await client.search('user preferences', 5);
memories.forEach(m => console.log(`- ${m.id}: ${m.content.slice(0, 50)}...`));
}
})();CLI Integration
The Trix CLI automatically uses the daemon when available. This provides:
- Faster queries - Uses local cache instead of API calls
- Offline support - Works without internet connection
- Lower latency - Sub-millisecond response for cached data
# These commands use the daemon automatically when running:
trix recall "user preferences" # Uses local cache
trix memories list # Uses local cache
trix context "project info" # Uses local cache
# Force API call (bypass daemon)
trix recall "query" --no-daemonIPC Error Codes
Standard JSON-RPC 2.0 error codes:
| Code | Message | Description |
|---|---|---|
-32700 | Parse error | Invalid JSON received |
-32600 | Invalid request | Not a valid JSON-RPC request |
-32601 | Method not found | Unknown method name |
-32602 | Invalid params | Invalid method parameters |
-32603 | Internal error | Internal daemon error |
-32000 | Not connected | Daemon not connected to API |
-32001 | Cache miss | Requested item not in cache |