Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions src/gateway/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@ export interface SyncResult {
}

/**
* Sync moltbot config from container to R2 for persistence.
* Sync moltbot config and workspace from container to R2 for persistence.
*
* This function:
* 1. Mounts R2 if not already mounted
* 2. Verifies source has critical files (prevents overwriting good backup with empty data)
* 3. Runs rsync to copy config to R2
* 3. Runs rsync to copy config and workspace to R2
* 4. Writes a timestamp file for tracking
*
* Persisted paths:
* - /root/.clawdbot/ -> R2/clawdbot/ (config, cron, credentials)
* - /root/clawd/ -> R2/clawd/ (workspace: memory, tools, custom files)
* - /root/clawd/skills/ -> R2/skills/ (legacy path for backwards compatibility)
*
* @param sandbox - The sandbox instance
* @param env - Worker environment bindings
* @returns SyncResult with success status and optional error details
Expand Down Expand Up @@ -57,9 +62,13 @@ export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise<SyncR
};
}

// Run rsync to backup config to R2
// Run rsync to backup config and workspace to R2
// Note: Use --no-times because s3fs doesn't support setting timestamps
const syncCmd = `rsync -r --no-times --delete --exclude='*.lock' --exclude='*.log' --exclude='*.tmp' /root/.clawdbot/ ${R2_MOUNT_PATH}/clawdbot/ && rsync -r --no-times --delete /root/clawd/skills/ ${R2_MOUNT_PATH}/skills/ && date -Iseconds > ${R2_MOUNT_PATH}/.last-sync`;
// Syncs:
// /root/.clawdbot/ -> R2/clawdbot/ (config, cron, credentials)
// /root/clawd/ -> R2/clawd/ (workspace: memory, tools, skills, etc.)
// /root/clawd/skills/ -> R2/skills/ (legacy: kept for backwards compatibility)
const syncCmd = `rsync -r --no-times --delete --exclude='*.lock' --exclude='*.log' --exclude='*.tmp' /root/.clawdbot/ ${R2_MOUNT_PATH}/clawdbot/ && rsync -r --no-times --delete --exclude='*.lock' --exclude='*.log' --exclude='*.tmp' --exclude='.git' --exclude='node_modules' /root/clawd/ ${R2_MOUNT_PATH}/clawd/ && rsync -r --no-times --delete /root/clawd/skills/ ${R2_MOUNT_PATH}/skills/ && date -Iseconds > ${R2_MOUNT_PATH}/.last-sync`;

try {
const proc = await sandbox.startProcess(syncCmd);
Expand Down
18 changes: 16 additions & 2 deletions start-moltbot.sh
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,25 @@ else
echo "R2 not mounted, starting fresh"
fi

# Restore skills from R2 backup if available (only if R2 is newer)
# Restore full workspace from R2 backup if available (only if R2 is newer)
# This includes memory, tools, custom files - everything the agent creates
WORKSPACE_DIR="/root/clawd"
if [ -d "$BACKUP_DIR/clawd" ] && [ "$(ls -A $BACKUP_DIR/clawd 2>/dev/null)" ]; then
if should_restore_from_r2; then
echo "Restoring workspace from $BACKUP_DIR/clawd..."
mkdir -p "$WORKSPACE_DIR"
cp -a "$BACKUP_DIR/clawd/." "$WORKSPACE_DIR/"
echo "Restored workspace from R2 backup"
fi
fi

# Restore skills from R2 backup if available (legacy path, only if R2 is newer)
# Note: Skills are also included in the workspace backup above, but we keep this
# for backwards compatibility with existing backups that only have /skills/
SKILLS_DIR="/root/clawd/skills"
if [ -d "$BACKUP_DIR/skills" ] && [ "$(ls -A $BACKUP_DIR/skills 2>/dev/null)" ]; then
if should_restore_from_r2; then
echo "Restoring skills from $BACKUP_DIR/skills..."
echo "Restoring skills from $BACKUP_DIR/skills (legacy path)..."
mkdir -p "$SKILLS_DIR"
cp -a "$BACKUP_DIR/skills/." "$SKILLS_DIR/"
echo "Restored skills from R2 backup"
Expand Down