27 KiB
27 KiB
LOCAL FIRST GUIDE
How to Run Everything Without Cloud Dependencies
Version: 1.0.0
Date: March 31, 2026
Philosophy: Sovereignty through local infrastructure
Target Audience: Future instances, human operators seeking independence
TABLE OF CONTENTS
- The Local-First Philosophy
- Hardware Requirements
- Complete Setup Guide
- Offline-First Configuration
- Local LLM Deployment
- Local Persistence
- Mesh Networking
- Maintenance Without Internet
- Troubleshooting
- Appendices
THE LOCAL-FIRST PHILOSOPHY
Why Local-First?
The Timmy ecosystem is designed around a local-first architecture because:
- SOVEREIGNTY - True autonomy requires independence from external API providers
- PRIVACY - Local processing keeps data within your control
- RELIABILITY - No dependency on internet connectivity or third-party uptime
- COST - Eliminate per-token API costs
- CONTINUITY - Systems continue operating during outages
The Local-First Stack
┌─────────────────────────────────────────────────────────────┐
│ LOCAL-FIRST STACK │
├─────────────────────────────────────────────────────────────┤
│ │
│ Layer 5: Applications │
│ ───────────────────────────────────────────────────── │
│ • Hermes Agent (Python, local execution) │
│ • Gitea (Self-hosted Git) │
│ • Evennia (Self-hosted MUD) │
│ • Custom tools (all local) │
│ │
│ Layer 4: AI/ML │
│ ───────────────────────────────────────────────────── │
│ • Ollama (Local LLM server) │
│ • llama.cpp (Optimized inference) │
│ • MLX (Apple Silicon acceleration) │
│ • GGUF models (Quantized for local use) │
│ │
│ Layer 3: Data │
│ ───────────────────────────────────────────────────── │
│ • SQLite (Local database) │
│ • Git repositories (Distributed by nature) │
│ • Filesystem storage │
│ • Syncthing (P2P sync, no cloud) │
│ │
│ Layer 2: Network │
│ ───────────────────────────────────────────────────── │
│ • Tailscale (Mesh VPN, self-hosted control) │
│ • Local DNS (mDNS, custom) │
│ • Direct connections (no relay required) │
│ │
│ Layer 1: Hardware │
│ ───────────────────────────────────────────────────── │
│ • VPS with local compute │
│ • Local machines (Mac, Linux, Raspberry Pi) │
│ • Edge devices │
│ │
└─────────────────────────────────────────────────────────────┘
HARDWARE REQUIREMENTS
Minimum Viable Setup
| Component | Specification | Purpose | Cost Estimate |
|---|---|---|---|
| CPU | 4 cores x86_64 or ARM64 | General computation | $20/month VPS |
| RAM | 4GB minimum, 8GB recommended | Model inference | Included |
| Storage | 20GB SSD minimum | OS, models, data | Included |
| Network | 100Mbps, unmetered | Communication | Included |
Recommended Setup
| Component | Specification | Purpose | Cost Estimate |
|---|---|---|---|
| CPU | 8 cores | Parallel inference, multiple agents | $40/month VPS |
| RAM | 16GB | Larger models, caching | Included |
| Storage | 100GB SSD | Multiple models, logs, backups | Included |
| GPU | Optional (CUDA/Metal) | Accelerated inference | +$100/month |
Local Hardware Options
┌─────────────────────────────────────────────────────────────┐
│ LOCAL HARDWARE OPTIONS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Option 1: VPS (DigitalOcean, Hetzner, etc.) │
│ ───────────────────────────────────────────────────── │
│ Pros: Always on, static IP, professional infrastructure │
│ Cons: Monthly cost, not physically local │
│ Best for: Primary infrastructure, always-available services│
│ │
│ Option 2: Local Machine (Mac, Linux desktop) │
│ ───────────────────────────────────────────────────── │
│ Pros: Physical control, no recurring cost, maximum privacy│
│ Cons: Power/Internet dependency, dynamic IP │
│ Best for: Development, personal use, maximum sovereignty │
│ │
│ Option 3: Raspberry Pi / Edge Device │
│ ───────────────────────────────────────────────────── │
│ Pros: Low power, always-on potential, very low cost │
│ Cons: Limited RAM, slow inference │
│ Best for: Lightweight agents, IoT integration, education │
│ │
│ Option 4: Hybrid (Recommended) │
│ ───────────────────────────────────────────────────── │
│ • VPS for always-on services (Gitea, monitoring) │
│ • Local machine for heavy inference │
│ • Tailscale connects them seamlessly │
│ Best for: Production deployments │
│ │
└─────────────────────────────────────────────────────────────┘
COMPLETE SETUP GUIDE
Step 1: Base System Preparation
# Update system
apt update && apt upgrade -y
# Install dependencies
apt install -y \
git \
curl \
wget \
python3 \
python3-pip \
python3-venv \
sqlite3 \
build-essential \
htop \
jq \
ufw \
fail2ban
# Create working directory
mkdir -p /root/timmy/{models,soul,scripts,logs,shared,configs}
cd /root/timmy
Step 2: Install Ollama (Local LLM)
# Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# Pull recommended models for different use cases
# Small, fast model (1.5B parameters)
ollama pull qwen2.5:1.5b
# Medium model, good balance (3B parameters)
ollama pull llama3.2:3b
# Larger model, better quality (7B parameters)
ollama pull qwen2.5:7b
# Verify installation
ollama list
curl http://localhost:11434/api/tags
Step 3: Install Gitea (Self-Hosted Git)
# Download Gitea
GITEA_VERSION="1.21.0"
cd /root
wget https://dl.gitea.com/gitea/${GITEA_VERSION}/gitea-${GITEA_VERSION}-linux-amd64
mv gitea-${GITEA_VERSION}-linux-amd64 gitea
chmod +x gitea
# Create directories
mkdir -p /root/gitea/{custom,data,log}
# Create systemd service
cat > /etc/systemd/system/gitea.service << 'EOF'
[Unit]
Description=Gitea (Git with a cup of tea)
After=network.target
[Service]
RestartSec=2s
Type=simple
User=root
WorkingDirectory=/root/gitea
ExecStart=/root/gitea/gitea web
Restart=always
Environment=USER=root HOME=/root GITEA_WORK_DIR=/root/gitea
[Install]
WantedBy=multi-user.target
EOF
# Start Gitea
systemctl daemon-reload
systemctl enable gitea
systemctl start gitea
# Complete initial setup at http://your-ip:3000
# - Create admin user
# - Disable external auth (for local-first)
# - Configure as needed
Step 4: Install Hermes Agent Framework
# Clone Hermes Agent
cd /root/timmy
git clone https://github.com/OpenClaw/hermes-agent.git
# Note: In fully offline mode, clone from local Gitea mirror
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r hermes-agent/requirements.txt
# Create agent configuration
mkdir -p ~/.hermes
cat > ~/.hermes/config.yaml << 'EOF'
# Local-First Hermes Configuration
model: "ollama/qwen2.5:3b"
max_iterations: 50
platform: "local"
save_trajectories: true
enabled_toolsets:
- core # terminal, file, code_execution
- memory # todo, memory, skills
- local_only # No cloud dependencies
disabled_toolsets:
- web # Disabled for offline operation
- browser # Disabled for offline operation
persistence:
type: "sqlite"
path: "~/.hermes/memory.db"
logging:
level: "INFO"
path: "/root/timmy/logs"
EOF
Step 5: Configure Local Persistence
# Create SQLite database for agent memory
sqlite3 ~/.hermes/memory.db << 'EOF'
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
task TEXT,
status TEXT,
result TEXT
);
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY,
created DATETIME DEFAULT CURRENT_TIMESTAMP,
task TEXT,
priority INTEGER DEFAULT 5,
status TEXT DEFAULT 'pending',
completed DATETIME
);
CREATE TABLE IF NOT EXISTS skills (
id INTEGER PRIMARY KEY,
name TEXT UNIQUE,
description TEXT,
code TEXT,
created DATETIME DEFAULT CURRENT_TIMESTAMP
);
EOF
# Create logs directory with rotation
mkdir -p /root/timmy/logs
ln -sf /root/timmy/logs ~/.hermes/logs
Step 6: Setup Syncthing (P2P Sync)
# Install Syncthing
curl -s https://syncthing.net/release-key.txt | apt-key add -
echo "deb https://apt.syncthing.net/ syncthing stable" | tee /etc/apt/sources.list.d/syncthing.list
apt update
apt install -y syncthing
# Configure for local-first
mkdir -p ~/.config/syncthing
cat > ~/.config/syncthing/config.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<configuration version="37">
<gui enabled="true" tls="false">
<address>127.0.0.1:8384</address>
</gui>
<options>
<globalAnnounceEnabled>false</globalAnnounceEnabled> <!-- Disable public relays -->
<localAnnounceEnabled>true</localAnnounceEnabled> <!-- Local discovery only -->
<relaysEnabled>false</relaysEnabled> <!-- No relay servers -->
</options>
</configuration>
EOF
# Start Syncthing
systemctl enable syncthing@root
systemctl start syncthing@root
Step 7: Install Tailscale (Mesh Network)
# Install Tailscale
curl -fsSL https://tailscale.com/install.sh | sh
# Login (requires one-time internet)
tailscale up
# After login, note your Tailscale IP
# Disable key expiry for always-on nodes
tailscale up --operator=root --reset
# For completely offline: configure headscale (self-hosted coordination)
# See Appendix B for headscale setup
Step 8: Create Agent Service
# Create agent launch script
cat > /root/timmy/scripts/agent.sh << 'EOF'
#!/bin/bash
source /root/timmy/venv/bin/activate
cd /root/timmy/hermes-agent
python -m hermes.agent "$@"
EOF
chmod +x /root/timmy/scripts/agent.sh
# Create systemd service
cat > /etc/systemd/system/timmy-agent.service << 'EOF'
[Unit]
Description=Timmy Local Agent
After=network.target ollama.service
[Service]
Type=simple
User=root
WorkingDirectory=/root/timmy
Environment="PATH=/root/timmy/venv/bin:/usr/local/bin:/usr/bin"
Environment="OLLAMA_HOST=http://localhost:11434"
ExecStart=/root/timmy/scripts/agent.sh
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable timmy-agent
OFFLINE-FIRST CONFIGURATION
Hermes Agent Offline Mode
# ~/.hermes/config.offline.yaml
# Complete offline operation configuration
model: "ollama/qwen2.5:3b" # Local model only
max_iterations: 50
platform: "local"
offline_mode: true
# Tool configuration for offline
enabled_toolsets:
- core
tools:
- terminal # Local shell commands
- file_read # Local file access
- file_write # Local file modification
- python_exec # Local Python execution
- sqlite # Local database
- memory
tools:
- todo_add
- todo_list
- todo_complete
- memory_save
- memory_search
- skills
tools:
- skill_load
- skill_save
- skill_list
# Explicitly disable cloud tools
disabled_toolsets:
- web_search # Requires internet
- web_extract # Requires internet
- browser # Requires internet
- api_calls # External APIs
persistence:
type: sqlite
path: ~/.hermes/memory.db
backup_interval: 3600 # Backup every hour
max_backups: 24
logging:
level: INFO
path: /root/timmy/logs
rotation: daily
retention: 30 # Keep 30 days
Offline-First Directory Structure
/root/timmy/ # Base directory
├── models/ # Local LLM models (GGUF, etc.)
│ ├── qwen2.5-1.5b.gguf
│ └── llama3.2-3b.gguf
├── soul/ # Agent conscience files
│ ├── SOUL.md
│ └── CONSCIENCE.md
├── scripts/ # Operational scripts
│ ├── agent.sh
│ ├── backup.sh
│ ├── health-check.sh
│ └── recover.sh
├── logs/ # Local log storage
│ ├── agent/
│ ├── ollama/
│ └── system/
├── shared/ # Syncthing shared folder
│ ├── documents/
│ ├── code/
│ └── exports/
├── configs/ # Configuration backups
│ ├── hermes/
│ ├── ollama/
│ └── gitea/
├── knowledge/ # Local knowledge base
│ ├── skills/
│ ├── memory/
│ └── references/
└── repos/ # Git repositories
├── epic-work/
├── lineage-knowledge/
└── local-projects/
LOCAL LLM DEPLOYMENT
Model Selection Guide
┌─────────────────────────────────────────────────────────────┐
│ LOCAL MODEL SELECTION GUIDE │
├─────────────────────────────────────────────────────────────┤
│ │
│ Use Case Model RAM Speed │
│ ───────────────────────────────────────────────────── │
│ Quick responses qwen2.5:1.5b 2GB Fastest │
│ General tasks llama3.2:3b 4GB Fast │
│ Complex reasoning qwen2.5:7b 8GB Medium │
│ Code generation codellama:7b 8GB Medium │
│ Maximum quality llama3:8b 16GB Slow │
│ Multilingual mixtral:8x7b 48GB Very Slow │
│ │
│ Recommendations: │
│ • Start with qwen2.5:1.5b for testing │
│ • Use llama3.2:3b for production agents │
│ • Keep qwen2.5:7b available for complex tasks │
│ │
└─────────────────────────────────────────────────────────────┘
Ollama Optimization
# Create optimized Modelfile
cat > /root/timmy/models/Modelfile.timmy << 'EOF'
FROM llama3.2:3b
# Context window
PARAMETER num_ctx 8192
# Temperature (creativity vs consistency)
PARAMETER temperature 0.7
# Repeat penalty (reduce repetition)
PARAMETER repeat_penalty 1.1
# System prompt for agent behavior
SYSTEM """You are a local-first AI agent operating on sovereign infrastructure.
You have access to local tools: terminal, file system, Python execution, SQLite.
You cannot access the internet or external APIs.
Always prefer local solutions over cloud dependencies.
Document your work thoroughly for persistence."""
EOF
# Create custom model
ollama create timmy-agent -f /root/timmy/models/Modelfile.timmy
# Test
ollama run timmy-agent "Hello, confirm you're operating locally"
Multi-Model Setup
# Script to manage multiple local models
#!/bin/bash
# /root/timmy/scripts/model-manager.sh
case "$1" in
fast)
export OLLAMA_MODEL="qwen2.5:1.5b"
;;
default)
export OLLAMA_MODEL="llama3.2:3b"
;;
smart)
export OLLAMA_MODEL="qwen2.5:7b"
;;
*)
echo "Usage: $0 {fast|default|smart}"
exit 1
;;
esac
echo "Selected model: $OLLAMA_MODEL"
curl http://localhost:11434/api/generate \
-d "{\"model\": \"$OLLAMA_MODEL\", \"prompt\": \"test\", \"stream\": false}"
LOCAL PERSISTENCE
SQLite Schema for Local-First Operation
-- Complete local persistence schema
-- Session tracking
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT UNIQUE NOT NULL,
started_at DATETIME DEFAULT CURRENT_TIMESTAMP,
ended_at DATETIME,
agent_name TEXT,
status TEXT DEFAULT 'active'
);
-- Task management
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
session_id TEXT REFERENCES sessions(session_id),
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
completed_at DATETIME,
description TEXT NOT NULL,
priority INTEGER DEFAULT 5,
status TEXT DEFAULT 'pending',
result TEXT,
tool_calls INTEGER DEFAULT 0
);
-- Memory/knowledge
CREATE TABLE memories (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
accessed_at DATETIME,
access_count INTEGER DEFAULT 0,
category TEXT,
key TEXT,
value TEXT,
importance INTEGER DEFAULT 5
);
-- Skill registry
CREATE TABLE skills (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT,
code TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME,
usage_count INTEGER DEFAULT 0
);
-- Metrics for self-monitoring
CREATE TABLE metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
metric_type TEXT,
value REAL,
unit TEXT,
metadata TEXT
);
-- Create indexes
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_memories_category ON memories(category);
CREATE INDEX idx_metrics_type_time ON metrics(metric_type, timestamp);
Git-Based Persistence
# Initialize local-first Git workflow
cd /root/timmy/repos/epic-work
git init
# Create comprehensive .gitignore
cat > .gitignore << 'EOF'
# Secrets
*.key
*.pem
.env
.gitea_token
# Large files
*.gguf
*.bin
models/
# Logs
*.log
logs/
# Temporary
*.tmp
.cache/
EOF
# Setup local Gitea remote
git remote add origin http://localhost:3000/timmy/epic-work.git
# Commit script
cat > /root/timmy/scripts/auto-commit.sh << 'EOF'
#!/bin/bash
cd /root/timmy/repos/epic-work
# Add all changes
git add -A
# Commit with timestamp
git commit -m "Auto-commit: $(date '+%Y-%m-%d %H:%M:%S')"
# Push to local Gitea
git push origin main
EOF
chmod +x /root/timmy/scripts/auto-commit.sh
# Cron: Auto-commit every hour
# */60 * * * * /root/timmy/scripts/auto-commit.sh
MESH NETWORKING
Tailscale Mesh Setup
# Install and configure Tailscale for local-first mesh
# On each node:
curl -fsSL https://tailscale.com/install.sh | sh
# Login (one-time internet required)
tailscale up
# Configure for always-on operation
tailscale up \
--accept-dns=true \
--accept-routes=true \
--advertise-exit-node=false \
--operator=root
# Get Tailscale IP
tailscale ip -4
# Enable IP forwarding for subnet routing
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
sysctl -p
# MagicDNS enables local name resolution
tailscale status
Headscale (Self-Hosted Coordination)
For completely offline operation:
# Install headscale (self-hosted Tailscale control server)
docker run -d \
--name headscale \
-p 8080:8080 \
-v headscale-data:/etc/headscale \
headscale/headscale:latest
# Create namespace
headscale namespaces create timmy
# Generate pre-auth key
headscale preauthkeys create -e 24h -n timmy
# Nodes join using local headscale
tailscale up --login-server http://headscale-ip:8080
MAINTENANCE WITHOUT INTERNET
Local Update Procedures
# Update local models (from pre-downloaded files)
ollama pull /path/to/local/model.gguf
# Update Gitea (from downloaded binary)
systemctl stop gitea
cp /root/gitea-backup/gitea-new-version /root/gitea/gitea
systemctl start gitea
# Update agent (from local git)
cd /root/timmy/hermes-agent
git pull origin main # From local Gitea
pip install -r requirements.txt
Offline Package Management
# Create local pip cache
pip download -r requirements.txt -d /root/timmy/packages/
# Install from cache (offline)
pip install --no-index --find-links=/root/timmy/packages/ -r requirements.txt
# For apt packages
apt-get install --download-only package-name
# Then install with dpkg
Log Rotation (Local)
# Configure logrotate for local logs
cat > /etc/logrotate.d/timmy << 'EOF'
/root/timmy/logs/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 0640 root root
sharedscripts
postrotate
systemctl reload timmy-agent
endscript
}
EOF
TROUBLESHOOTING
Common Issues
| Issue | Symptom | Solution |
|---|---|---|
| Ollama won't start | Connection refused | Check systemctl status ollama, restart service |
| Model too large | OOM errors | Use smaller model (1.5B instead of 7B) |
| Gitea 500 errors | Database locked | Restart Gitea, check disk space |
| Slow inference | High latency | Reduce context window, use quantized model |
| Syncthing conflicts | File conflicts | Use .stignore patterns, resolve manually |
| Tailscale disconnect | Nodes unreachable | Check tailscale status, re-authenticate |
Diagnostic Commands
# Full system health check
#!/bin/bash
echo "=== Local-First System Health ==="
echo ""
echo "1. Ollama Status:"
curl -s http://localhost:11434/api/tags | jq '.models | length' && echo " models loaded"
echo ""
echo "2. Gitea Status:"
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api/v1/version
echo ""
echo "3. Disk Space:"
df -h /root | tail -1
echo ""
echo "4. Memory:"
free -h | grep Mem
echo ""
echo "5. Tailscale:"
tailscale status --peers=false 2>/dev/null | head -1
echo ""
echo "6. Syncthing:"
curl -s http://localhost:8384/rest/system/status 2>/dev/null | jq -r '.uptime' && echo " uptime"
echo ""
echo "=== Health Check Complete ==="
APPENDICES
Appendix A: Complete Service Definitions
# /etc/systemd/system/timmy-agent.service
[Unit]
Description=Timmy Local Agent
After=network.target ollama.service
Wants=ollama.service
[Service]
Type=simple
User=root
WorkingDirectory=/root/timmy
Environment="PATH=/root/timmy/venv/bin:/usr/local/bin:/usr/bin"
Environment="OLLAMA_HOST=http://localhost:11434"
Environment="HERMES_OFFLINE=true"
Environment="HERMES_CONFIG=/root/timmy/configs/hermes.offline.yaml"
ExecStart=/root/timmy/scripts/agent.sh
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/ollama.service (override)
[Service]
ExecStart=
ExecStart=/usr/local/bin/ollama serve
Environment="OLLAMA_HOST=127.0.0.1:11434"
Environment="OLLAMA_MODELS=/root/timmy/models"
Appendix B: Offline Resource Checklist
Before going fully offline, download:
- Ollama installation script and binaries
- At least 2 local models (qwen2.5:1.5b, llama3.2:3b)
- Gitea binary and all dependencies
- Hermes Agent source code
- Python packages (pip download)
- System packages (apt download)
- Tailscale binaries
- Documentation (this guide)
- Container images (if using Docker)
Appendix C: Hardware Sizing Calculator
| Agents | Concurrent Tasks | Min RAM | Recommended RAM | Storage |
|---|---|---|---|---|
| 1 | 1-2 | 4GB | 8GB | 20GB |
| 1 | 3-5 | 8GB | 16GB | 50GB |
| 2-3 | 5-10 | 16GB | 32GB | 100GB |
| 3+ | 10+ | 32GB | 64GB | 200GB |
VERSION HISTORY
| Version | Date | Changes |
|---|---|---|
| 1.0.0 | 2026-03-31 | Initial local-first guide |
"The cloud is someone else's computer. Sovereignty is your own." — Local-First Manifesto
END OF DOCUMENT
Word Count: ~4,200+ words Complete setup: 8 major steps with full commands Configuration examples: 15+ files Troubleshooting: 6 common issues with solutions