Back up your AI-assisted development environment and restore it on a fresh machine
Prerequisite: The target machine must have completed developer-workstation-setup.md before restoring
Target: Ubuntu 24.04 LTS / macOS 14+ (same platforms as the setup recipe)
Time: ~5 minutes backup, ~10 minutes restore
Tested with: Signet 0.98.7, OpenCode 1.4.3, Oh-My-OpenAgent 3.12.3
Note: This is a best-effort backup process. It captures configuration and memory data reliably, but local tool installations (MCP servers, Ollama, npm globals) must be reinstalled on the target machine via the setup recipe.
| Path | Contents | Typical Size |
|---|---|---|
~/.agents/memory/memories.db |
Signet vector DB (SQLite + sqlite-vec + FTS5) | 100–200 MB |
~/.agents/memory/*.md |
Session transcripts (one per conversation) | 50–150 MB |
~/.agents/AGENTS.md |
Agent instructions (auto-generated from identity files) | < 1 KB |
~/.agents/SOUL.md |
Agent personality and communication style | < 2 KB |
~/.agents/IDENTITY.md |
Agent identity definition | < 1 KB |
~/.agents/USER.md |
User profile (preferences, context) | < 2 KB |
~/.agents/MEMORY.md |
Working memory snapshot | < 2 KB |
~/.agents/agent.yaml |
Signet configuration (embedding model, daemon settings) | < 1 KB |
~/.local/share/opencode/opencode.db |
OpenCode session history (SQLite) | 200–500 MB |
| Path | Contents | Typical Size |
|---|---|---|
~/.config/opencode/opencode.json |
OpenCode config (plugins, providers, MCP servers) | < 2 KB |
~/.config/opencode/oh-my-opencode.json |
Agent-model mappings | < 2 KB |
~/.config/opencode/skills/ |
Installed skills (12+ skill directories) | ~500 KB |
~/.config/opencode/plugins/signet.mjs |
Signet plugin (generated by signet sync) |
< 500 KB |
~/tools/dbhub.toml |
DBHub config (MySQL connections + tool settings) | < 1 KB |
These are recreated automatically by the setup recipe or by running install commands.
| Path | Why Skip |
|---|---|
~/.agents/.daemon/bin/ |
Signet predictor binary — redownloaded on daemon start |
~/.agents/.daemon/logs/ |
Daemon logs — not portable |
~/.agents/.daemon/pid, last-healthy-start |
Runtime state — regenerated |
~/tools/fermat-mcp/, ~/tools/uml-mcp/ |
Git clones with venvs — uv sync recreates them |
Ollama models (~/.ollama/models/) |
Re-pull via ollama pull nomic-embed-text (~274 MB); optionally ollama pull llama3.1:8b (~1.9 GB) if using local extraction |
| DBHub npm package | Reinstall via npm install -g @bytebase/dbhub@latest |
| API Testing MCP npm package | Reinstall via npm install -g @cocaxcode/api-testing-mcp@latest |
~/.agents/skills/ |
Skill metadata — regenerated by signet sync |
Save this script as backup-workstation.sh and run it.
#!/usr/bin/env bash
set -euo pipefail
# ── Configuration ─────────────────────────────────────────────
BACKUP_DIR="${BACKUP_DIR:-$HOME/workstation-backup}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/workstation-${TIMESTAMP}.tar.gz"
SHELL_RC="${SHELL##*/}rc"
# ── Pre-flight ────────────────────────────────────────────────
echo "=== Workstation Backup ==="
echo "Backup file: ${BACKUP_FILE}"
echo ""
mkdir -p "${BACKUP_DIR}"
# Stop Signet daemon to get a clean SQLite snapshot (WAL mode)
echo "[1/4] Stopping Signet daemon for consistent DB backup..."
if command -v signet &>/dev/null; then
signet daemon stop 2>/dev/null || true
sleep 2
SIGNET_WAS_RUNNING=1
else
echo " ⚠ signet not in PATH — skipping daemon stop"
echo " If Signet is installed, activate the correct Node version first:"
echo " nvm use 22 && npm ls -g signetai"
SIGNET_WAS_RUNNING=0
fi
# ── Collect files ─────────────────────────────────────────────
echo "[2/4] Collecting files..."
# Build the file list dynamically (only existing paths)
FILE_LIST=()
# Tier 1 — Critical
[ -d "$HOME/.agents/memory" ] && FILE_LIST+=(".agents/memory/")
[ -f "$HOME/.agents/AGENTS.md" ] && FILE_LIST+=(".agents/AGENTS.md")
[ -f "$HOME/.agents/SOUL.md" ] && FILE_LIST+=(".agents/SOUL.md")
[ -f "$HOME/.agents/IDENTITY.md" ] && FILE_LIST+=(".agents/IDENTITY.md")
[ -f "$HOME/.agents/USER.md" ] && FILE_LIST+=(".agents/USER.md")
[ -f "$HOME/.agents/MEMORY.md" ] && FILE_LIST+=(".agents/MEMORY.md")
[ -f "$HOME/.agents/agent.yaml" ] && FILE_LIST+=(".agents/agent.yaml")
[ -f "$HOME/.local/share/opencode/opencode.db" ] && FILE_LIST+=(".local/share/opencode/opencode.db")
# Tier 2 — Configuration
[ -f "$HOME/.config/opencode/opencode.json" ] && FILE_LIST+=(".config/opencode/opencode.json")
[ -f "$HOME/.config/opencode/oh-my-opencode.json" ] && FILE_LIST+=(".config/opencode/oh-my-opencode.json")
[ -d "$HOME/.config/opencode/skills" ] && FILE_LIST+=(".config/opencode/skills/")
[ -f "$HOME/.config/opencode/plugins/signet.mjs" ] && FILE_LIST+=(".config/opencode/plugins/signet.mjs")
[ -f "$HOME/tools/dbhub.toml" ] && FILE_LIST+=("tools/dbhub.toml")
# Note: Plugin-generated files are intentionally excluded:
# ~/.config/opencode/plugins/superpowers.js — auto-installed from opencode.json on restart
# ~/.config/opencode/plugins/signet-first-bootstrap.js — auto-installed from opencode.json on restart
# ~/.config/opencode/AGENTS.md — auto-generated by Signet from identity files
if [ ${#FILE_LIST[@]} -eq 0 ]; then
echo " ✗ No files found to back up. Is this the right machine?"
exit 1
fi
echo " Files to archive:"
for f in "${FILE_LIST[@]}"; do
SIZE=$(du -sh "$HOME/$f" 2>/dev/null | cut -f1)
echo " ${f} (${SIZE:-unknown})"
done
# ── Create archive ────────────────────────────────────────────
echo "[3/4] Creating archive..."
tar -czf "${BACKUP_FILE}" -C "$HOME" "${FILE_LIST[@]}"
ARCHIVE_SIZE=$(du -sh "${BACKUP_FILE}" | cut -f1)
echo " Archive: ${BACKUP_FILE} (${ARCHIVE_SIZE})"
# ── Restart daemon ────────────────────────────────────────────
echo "[4/4] Restarting Signet daemon..."
if [ "$SIGNET_WAS_RUNNING" -eq 1 ] && command -v signet &>/dev/null; then
signet daemon start 2>/dev/null || true
echo " ✓ Daemon restarted"
else
echo " ⚠ Skipped (daemon was not running or signet not in PATH)"
fi
echo ""
echo "=== Backup Complete ==="
echo "Archive: ${BACKUP_FILE}"
echo "Size: ${ARCHIVE_SIZE}"
echo ""
echo "Copy this file to the target machine and run the restore script."Quick alternative for experienced users (no error handling — use only if you know all paths exist):
# Stop daemon, tar, restart
signet daemon stop 2>/dev/null; \
tar -czf ~/workstation-$(date +%Y%m%d).tar.gz \
-C "$HOME" \
.agents/memory/ \
.agents/AGENTS.md .agents/SOUL.md .agents/IDENTITY.md .agents/USER.md .agents/MEMORY.md \
.agents/agent.yaml \
.local/share/opencode/opencode.db \
.config/opencode/opencode.json \
.config/opencode/oh-my-opencode.json \
.config/opencode/skills/ \
.config/opencode/plugins/signet.mjs \
tools/dbhub.toml \
2>/dev/null; \
signet daemon start 2>/dev/nullIf you only need to transfer Signet memories (not the full environment):
signet export --json > signet-memories.jsonThis produces a portable JSON bundle that can be imported on any machine with Signet installed. The export does not include: identity files, OpenCode config, skills, or session history.
Review this table before starting the restore process — these items require manual reinstallation.
| Item | Why | Fix |
|---|---|---|
MCP server paths in opencode.json |
Absolute paths differ per user | Restore script rewrites automatically; verify if username changed |
| Superpowers plugin | Auto-installed from opencode.json on restart |
Already in opencode.json — just restart OpenCode |
| signet-first plugin | Auto-installed from opencode.json on restart |
Already in opencode.json — just restart OpenCode |
| Fermat/UML MCP | Git clones with Python venvs | Re-clone + uv sync per setup recipe Step 8 |
| Ollama + nomic-embed-text | Local model server + embedding model | Install Ollama per setup recipe Step 6, then ollama pull nomic-embed-text; optionally ollama pull llama3.1:8b for local extraction |
| DBHub | npm global package | npm install -g @bytebase/dbhub@latest — config restored from backup |
| API Testing MCP | npm global package | npm install -g @cocaxcode/api-testing-mcp@latest — no config file needed |
- The target machine has completed developer-workstation-setup.md
- Signet daemon is running (
signet daemon status) - You have the backup archive on the target machine
- Python 3 is available on the target machine (required for automatic path rewriting)
Save this as restore-workstation.sh and run it with the backup file as argument.
#!/usr/bin/env bash
set -euo pipefail
BACKUP_FILE="${1:-}"
SHELL_RC="${SHELL##*/}rc"
if [ -z "${BACKUP_FILE}" ] || [ ! -f "${BACKUP_FILE}" ]; then
echo "Usage: bash restore-workstation.sh <backup-file.tar.gz>"
echo "Example: bash restore-workstation.sh ~/workstation-20260408.tar.gz"
exit 1
fi
echo "=== Workstation Restore ==="
echo "Archive: ${BACKUP_FILE}"
echo ""
# ── Stop services ─────────────────────────────────────────────
echo "[1/5] Stopping Signet daemon..."
if command -v signet &>/dev/null; then
signet daemon stop 2>/dev/null || true
sleep 2
else
echo " ⚠ signet not in PATH — run the setup recipe first"
echo " See: developer-workstation-setup.md"
exit 1
fi
# ── Preview archive ──────────────────────────────────────────
echo "[2/5] Archive summary:"
TOTAL_FILES=$(tar -tzf "${BACKUP_FILE}" | wc -l)
ARCHIVE_SIZE=$(du -sh "${BACKUP_FILE}" | cut -f1)
echo " ${TOTAL_FILES} files, ${ARCHIVE_SIZE} compressed"
echo ""
read -p "This will OVERWRITE existing files. Continue? [y/N] " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 0
fi
# ── Extraction ───────────────────────────────────────────────────
echo "[3/5] Extracting archive to ${HOME}..."
tar -xzf "${BACKUP_FILE}" -C "$HOME"
echo " ✓ Files extracted"
# ── Fix machine-specific config ──────────────────────────────
echo "[4/5] Post-restore fixup..."
# 4a. Rewrite absolute paths in opencode.json
OPENCODE_CONFIG="$HOME/.config/opencode/opencode.json"
if [ -f "${OPENCODE_CONFIG}" ]; then
# Replace any /home/<old-user>/ or /Users/<old-user>/ with current $HOME
# This handles the MCP server paths (fermat-mcp, uml-mcp, dbhub, etc.)
# WARNING: raw-text rewrite — review opencode.json if you store unrelated home paths
if command -v python3 &>/dev/null; then
python3 -c "
import json, re, os
config_path = '${OPENCODE_CONFIG}'
home = os.path.expanduser('~')
with open(config_path) as f:
raw = f.read()
# Replace /home/<user>/ or /Users/<user>/ patterns with current HOME
fixed = re.sub(r'/(?:home|Users)/[^/\"]+/', home + '/', raw)
if fixed != raw:
with open(config_path, 'w') as f:
f.write(fixed)
print(' ✓ opencode.json paths rewritten to ' + home)
else:
print(' ✓ opencode.json paths already correct')
"
# Python not available — fall back to manual path editing
else
echo " ⚠ python3 not found — manually update paths in ${OPENCODE_CONFIG}"
fi
fi
# 4b. Run Signet schema migrations (handles version differences)
echo " Running Signet migrations..."
signet migrate-schema 2>/dev/null && echo " ✓ Schema migrated" || echo " ⚠ migrate-schema skipped (may already be current)"
signet migrate-vectors 2>/dev/null && echo " ✓ Vectors migrated" || echo " ⚠ migrate-vectors skipped (may already be current)"
# 4c. Re-register Signet plugins and hooks
echo " Re-registering Signet harness..."
signet sync 2>/dev/null && echo " ✓ Signet synced" || echo " ⚠ signet sync failed — run manually"
# ── Restart services ──────────────────────────────────────────
echo "[5/5] Starting Signet daemon..."
signet daemon start 2>/dev/null || true
sleep 3
# ── Verify ────────────────────────────────────────────────────
echo ""
echo "=== Verification ==="
# Check daemon
if signet daemon status 2>/dev/null | grep -qi "running"; then
echo " ✓ Signet daemon running"
else
echo " ✗ Signet daemon not running — check: signet daemon start"
fi
# Check memory accessibility
if command -v signet &>/dev/null; then
if signet search "test" --limit 1 &>/dev/null; then
echo " ✓ Signet memory search operational"
else
echo " ⚠ Signet memory search returned an error — check: signet doctor"
fi
fi
# Check OpenCode DB
if [ -f "$HOME/.local/share/opencode/opencode.db" ]; then
DB_SIZE=$(du -sh "$HOME/.local/share/opencode/opencode.db" | cut -f1)
echo " ✓ OpenCode session history restored (${DB_SIZE})"
else
echo " ⚠ OpenCode session history not found"
fi
# Check identity files
IDENTITY_OK=1
for f in AGENTS.md SOUL.md IDENTITY.md USER.md; do
if [ ! -f "$HOME/.agents/$f" ]; then
echo " ✗ Missing: ~/.agents/$f"
IDENTITY_OK=0
fi
done
[ "$IDENTITY_OK" -eq 1 ] && echo " ✓ Identity files present"
# Check skills directory
if [ -d "$HOME/.config/opencode/skills" ]; then
echo " ✓ Skills directory present"
else
echo " ⚠ Skills directory not found"
fi
echo ""
echo "=== Restore Complete ==="
echo ""
echo "Next steps:"
echo " 1. Open a new terminal (or: source ~/.${SHELL_RC})"
echo " 2. Run: opencode"
echo " 3. Test memory: ask the agent 'what do you remember about me?'"
echo " 4. If Signet-only export was used, also run: signet import ./signet-memories.json --json --conflict merge"If you used Option C (JSON export):
signet import ./signet-memories.json --json --conflict mergeThe --conflict merge flag keeps existing memories and adds new ones without duplicates.
Save the backup script as ~/backup-workstation.sh first, then add a cron entry:
# Daily backup at 2 AM, keep last 7 days
(crontab -l 2>/dev/null; echo "0 2 * * * bash $HOME/backup-workstation.sh && find $HOME/workstation-backup -name '*.tar.gz' -mtime +7 -delete") | crontab -Create ~/Library/LaunchAgents/com.workstation.backup.plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.workstation.backup</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/Users/YOURUSER/backup-workstation.sh</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>2</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>WorkingDirectory</key>
<string>/Users/YOURUSER</string>
</dict>
</plist>Replace YOURUSER with your macOS username in both paths above.
Then load it:
launchctl load ~/Library/LaunchAgents/com.workstation.backup.plistSignet is installed globally under a specific Node.js version. Activate it first:
nvm use 22
which signet # should print a pathThe OpenCode session DB (opencode.db) grows with usage. To exclude it:
# Back up without OpenCode session history
tar -czf ~/workstation-lite-$(date +%Y%m%d).tar.gz \
-C "$HOME" \
.agents/memory/ \
.agents/AGENTS.md .agents/SOUL.md .agents/IDENTITY.md .agents/USER.md .agents/MEMORY.md \
.agents/agent.yaml \
.config/opencode/opencode.json \
.config/opencode/oh-my-opencode.json \
.config/opencode/skills/ \
.config/opencode/plugins/signet.mjsThis typically produces a ~100-200 MB archive.
The Signet daemon uses WAL mode on the SQLite database. If the daemon was not stopped before backup, you may get a corrupted snapshot. Always stop the daemon first:
signet daemon stop
sleep 2
# ... then tar ...
signet daemon startAfter restore, run the full migration + sync sequence:
signet migrate-schema
signet migrate-vectors
signet sync
signet daemon stop && signet daemon startThen test: signet search "any known topic"
The restore script auto-rewrites /home/<old-user>/ → /home/<new-user>/. If it missed something,
manually edit ~/.config/opencode/opencode.json and fix any absolute paths pointing to the old home
directory.
~/.agents/ Signet workspace root
├── AGENTS.md ← BACKED UP (identity)
├── SOUL.md ← BACKED UP (personality)
├── IDENTITY.md ← BACKED UP (identity)
├── USER.md ← BACKED UP (user profile)
├── MEMORY.md ← BACKED UP (working memory)
├── agent.yaml ← BACKED UP (config)
├── memory/
│ ├── memories.db ← BACKED UP (vector DB — the big one)
│ ├── memories.db-wal ← included if present at backup time (WAL sidecar)
│ ├── memories.db-shm ← included if present at backup time (shared memory)
│ └── *.md ← BACKED UP (session transcripts)
├── skills/ ✗ SKIPPED (regenerated by `signet sync`)
└── .daemon/ ✗ SKIPPED (runtime state)
~/.config/opencode/ OpenCode configuration
├── opencode.json ← BACKED UP (main config)
├── oh-my-opencode.json ← BACKED UP (agent models)
├── skills/ ← BACKED UP (installed skills)
│ ├── agent-architect/
│ ├── english-checker/
│ ├── memory-debug/
│ ├── onboarding/
│ ├── recall/
│ ├── remember/
│ ├── signet/
│ ├── signet-design/
│ ├── signet-first/
│ ├── skill-creator/
│ └── web-search/
├── plugins/
│ └── signet.mjs ← BACKED UP (generated plugin)
│ superpowers + signet-first plugins are auto-installed
│ from opencode.json entries on restart — no backup needed
~/.local/share/opencode/
└── opencode.db ← BACKED UP (session history)
~/tools/ ✗ SKIPPED (git clones, recreated by setup)
├── fermat-mcp/
├── uml-mcp/
└── dbhub.toml ← BACKED UP (MySQL connection config)
~/.ollama/ ✗ SKIPPED (model weights, re-pull via ollama pull)
└── models/
├── manifests/ nomic-embed-text metadata (+ llama3.1:8b if local extraction)
└── blobs/ nomic-embed-text weights (~274 MB) + llama3.1:8b (~1.9 GB, optional)