openclaw-workspace-sync 2.2.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +395 -262
- package/README.src.md +412 -262
- package/dist/backup-manager.d.ts +67 -0
- package/dist/backup-manager.d.ts.map +1 -0
- package/dist/backup-manager.js +520 -0
- package/dist/backup-manager.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +247 -27
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +82 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/diagrams/mode-3.svg +42 -0
- package/openclaw.plugin.json +122 -1
- package/package.json +2 -2
- package/skills/workspace-sync/SKILL.md +57 -10
package/README.src.md
CHANGED
|
@@ -1,31 +1,108 @@
|
|
|
1
|
-
# OpenClaw Workspace
|
|
1
|
+
# OpenClaw Workspace Sync & Backup Plugin
|
|
2
2
|
|
|
3
|
-
Sync your OpenClaw agent workspace
|
|
3
|
+
Sync and back up your OpenClaw agent workspace to cloud storage via [rclone](https://rclone.org/).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
**Sync** your workspace to Dropbox, Google Drive, OneDrive, S3, or [70+ providers](https://rclone.org/overview/) with mailbox, mirror, or bisync modes. **Back up** your entire agent system — workspace, config, sessions, memory — as encrypted snapshots to S3, R2, B2, or any rclone backend.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## What's included
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
| Feature | What it does | Cost |
|
|
10
|
+
|---------|-------------|------|
|
|
11
|
+
| [**Sync**](#sync) | Live mirror of your workspace to/from cloud storage. Three modes: mailbox (safest), mirror, bisync. | Zero LLM cost — pure file ops |
|
|
12
|
+
| [**Encrypted Backup**](#encrypted-backups) | Streaming encrypted snapshots of your entire agent system to your own bucket. Automatic retention. | Zero LLM cost, zero extra disk |
|
|
13
|
+
|
|
14
|
+
Both features use rclone under the hood and share provider credentials. You can use sync alone, backup alone, or both together with different providers — e.g. sync to Dropbox for daily access, backup to R2 for disaster recovery.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
openclaw plugins install openclaw-workspace-sync
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or clone into your extensions directory:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
cd ~/.openclaw/extensions
|
|
26
|
+
git clone https://github.com/ashbrener/openclaw-workspace-sync workspace-sync
|
|
27
|
+
cd workspace-sync && npm install --omit=dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Interactive setup wizard (recommended)
|
|
34
|
+
openclaw workspace-sync setup
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The setup wizard guides you through:
|
|
38
|
+
1. Checking/installing rclone
|
|
39
|
+
2. Selecting cloud provider
|
|
40
|
+
3. Choosing sync mode
|
|
41
|
+
4. Dropbox app folder option (for scoped access)
|
|
42
|
+
5. Background sync interval
|
|
43
|
+
6. OAuth authorization
|
|
44
|
+
7. First sync
|
|
45
|
+
|
|
46
|
+
Or configure manually — see [Configuration](#configuration) below.
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
Add to your `openclaw.json`. The `sync` and `backup` blocks are independent — use one or both:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"plugins": {
|
|
55
|
+
"entries": {
|
|
56
|
+
"openclaw-workspace-sync": {
|
|
57
|
+
"enabled": true,
|
|
58
|
+
"config": {
|
|
59
|
+
"sync": {
|
|
60
|
+
"provider": "dropbox",
|
|
61
|
+
"mode": "mailbox",
|
|
62
|
+
"remotePath": "",
|
|
63
|
+
"localPath": "/",
|
|
64
|
+
"interval": 180,
|
|
65
|
+
"onSessionStart": true,
|
|
66
|
+
"exclude": [".git/**", "node_modules/**", "*.log"]
|
|
67
|
+
},
|
|
68
|
+
"backup": {
|
|
69
|
+
"enabled": true,
|
|
70
|
+
"provider": "s3",
|
|
71
|
+
"bucket": "my-backups",
|
|
72
|
+
"prefix": "habibi/",
|
|
73
|
+
"interval": 86400,
|
|
74
|
+
"encrypt": true,
|
|
75
|
+
"passphrase": "${BACKUP_PASSPHRASE}",
|
|
76
|
+
"include": ["workspace", "config", "cron", "memory"],
|
|
77
|
+
"retain": 7
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> **Flat format still works.** Putting `provider`, `mode`, etc. at the config root (without `sync`) is supported for backwards compatibility. The nested `{ sync, backup }` format is recommended for clarity.
|
|
12
87
|
|
|
13
|
-
|
|
88
|
+
---
|
|
14
89
|
|
|
15
|
-
|
|
90
|
+
## Sync
|
|
16
91
|
|
|
17
|
-
|
|
92
|
+
Live workspace mirroring via rclone. The remote gateway workspace is the **source of truth**. Changes made by the agent flow down to your local machine through cloud storage. You can send files to the agent through an optional inbox.
|
|
93
|
+
|
|
94
|
+
<p align="center">
|
|
95
|
+
<img src="https://raw.githubusercontent.com/ashbrener/openclaw-workspace-sync/main/docs/how-it-works.png" alt="How it works — Local Machine syncs to Cloud Provider syncs to Remote Gateway" width="600" />
|
|
96
|
+
</p>
|
|
18
97
|
|
|
19
98
|
<p align="center">
|
|
20
99
|
<img src="https://raw.githubusercontent.com/ashbrener/openclaw-workspace-sync/main/docs/architecture.png" alt="Plugin architecture — CLI, Hooks, and Sync Manager feed into rclone wrapper" width="600" />
|
|
21
100
|
</p>
|
|
22
101
|
|
|
23
|
-
|
|
102
|
+
### Sync modes (breaking change in v2.0)
|
|
24
103
|
|
|
25
104
|
**`mode` is now required.** Previous versions used bidirectional bisync implicitly. Starting with v2.0, you must explicitly set `"mode"` in your config. The plugin will refuse to start and log an error until `mode` is set. This prevents accidental data loss from an unexpected sync direction.
|
|
26
105
|
|
|
27
|
-
The plugin supports three sync modes. Choose the one that fits your workflow:
|
|
28
|
-
|
|
29
106
|
| Mode | Direction | Description |
|
|
30
107
|
|------|-----------|-------------|
|
|
31
108
|
| `mailbox` | Push + inbox/outbox | Workspace pushes to cloud; users drop files in `_outbox` to send them to the agent. **Safest.** |
|
|
@@ -34,7 +111,7 @@ The plugin supports three sync modes. Choose the one that fits your workflow:
|
|
|
34
111
|
|
|
35
112
|
**Upgrading from a previous version?** If you were using bisync before, add `"mode": "bisync"` to your config to preserve the existing behavior. For the safest option, use `"mode": "mailbox"`.
|
|
36
113
|
|
|
37
|
-
|
|
114
|
+
#### `mailbox` mode (recommended)
|
|
38
115
|
|
|
39
116
|
The agent workspace is the source of truth. Each sync cycle:
|
|
40
117
|
|
|
@@ -72,7 +149,7 @@ On startup, the plugin bootstraps both directories:
|
|
|
72
149
|
|
|
73
150
|
Because the push explicitly excludes `_inbox/**` and `_outbox/**`, there is no risk of sync loops or accidental overwrites. Files only flow in one direction through each channel.
|
|
74
151
|
|
|
75
|
-
|
|
152
|
+
##### Inbox notifications (optional)
|
|
76
153
|
|
|
77
154
|
By default, mailbox mode is silent — files land in `_inbox` without waking the agent. This keeps costs at zero.
|
|
78
155
|
|
|
@@ -95,7 +172,7 @@ This wakes the agent on its next heartbeat. The agent sees the message and can p
|
|
|
95
172
|
}
|
|
96
173
|
```
|
|
97
174
|
|
|
98
|
-
|
|
175
|
+
#### `mirror` mode
|
|
99
176
|
|
|
100
177
|
The agent workspace is the source of truth. Every sync cycle copies the latest workspace state down to your local folder. Local files outside the workspace are never sent up.
|
|
101
178
|
|
|
@@ -117,7 +194,7 @@ flowchart LR
|
|
|
117
194
|
|
|
118
195
|
This is safe: even if something goes wrong, only your local copy is affected — the workspace stays untouched.
|
|
119
196
|
|
|
120
|
-
|
|
197
|
+
##### `ingest` option (mirror mode only)
|
|
121
198
|
|
|
122
199
|
Want to send files to the agent while using mirror mode? Enable the `ingest` option. This creates a local `inbox/` folder (sibling to the sync folder) that syncs one-way **up** to the workspace. Drop a file in the inbox — it appears on the remote workspace. The inbox is separate from the mirror, so there is no risk of overwriting workspace files.
|
|
123
200
|
|
|
@@ -133,7 +210,7 @@ When enabled, a local `inbox/` folder syncs its contents to `<remotePath>/inbox/
|
|
|
133
210
|
|
|
134
211
|
> For a more robust file-exchange pattern, consider `mailbox` mode instead. Mailbox uses `rclone move` to drain files (deleting from the source after transfer), which prevents duplicates and is easier to reason about.
|
|
135
212
|
|
|
136
|
-
|
|
213
|
+
#### `bisync` mode (advanced)
|
|
137
214
|
|
|
138
215
|
Full bidirectional sync using rclone bisync. Changes on either side propagate to the other.
|
|
139
216
|
|
|
@@ -164,11 +241,99 @@ Use this only if you understand the trade-offs:
|
|
|
164
241
|
|
|
165
242
|
If you are running on a container platform, `mailbox` mode is strongly recommended.
|
|
166
243
|
|
|
167
|
-
|
|
244
|
+
### Sync config reference
|
|
245
|
+
|
|
246
|
+
| Key | Type | Default | Description |
|
|
247
|
+
|-----|------|---------|-------------|
|
|
248
|
+
| `provider` | string | `"off"` | `dropbox` \| `gdrive` \| `onedrive` \| `s3` \| `custom` \| `off` |
|
|
249
|
+
| `mode` | string | **required** | `mailbox` \| `mirror` \| `bisync` — see [Sync modes](#sync-modes-breaking-change-in-v20) |
|
|
250
|
+
| `ingest` | boolean | `false` | Enable local inbox for sending files to the agent (mirror mode only) |
|
|
251
|
+
| `ingestPath` | string | `"inbox"` | Local subfolder name for ingestion (relative to `localPath`) |
|
|
252
|
+
| `notifyOnInbox` | boolean | `false` | Wake the agent when files arrive in `_inbox` (mailbox mode). Off by default — enabling this costs LLM credits per notification. |
|
|
253
|
+
| `remotePath` | string | `"openclaw-share"` | Folder name in cloud storage |
|
|
254
|
+
| `localPath` | string | `"shared"` | Subfolder within workspace to sync |
|
|
255
|
+
| `interval` | number | `0` | Background sync interval in seconds (0 = manual only, min 60) |
|
|
256
|
+
| `timeout` | number | `1800` | Max seconds for a single rclone sync operation (min 60) |
|
|
257
|
+
| `onSessionStart` | boolean | `false` | Sync when an agent session begins |
|
|
258
|
+
| `onSessionEnd` | boolean | `false` | Sync when an agent session ends |
|
|
259
|
+
| `remoteName` | string | `"cloud"` | rclone remote name |
|
|
260
|
+
| `configPath` | string | auto | Path to rclone.conf |
|
|
261
|
+
| `conflictResolve` | string | `"newer"` | `newer` \| `local` \| `remote` (bisync only) |
|
|
262
|
+
| `exclude` | string[] | see below | Glob patterns to exclude |
|
|
263
|
+
| `copySymlinks` | boolean | `false` | Follow symlinks during sync |
|
|
264
|
+
|
|
265
|
+
Default excludes: `**/.DS_Store`
|
|
266
|
+
|
|
267
|
+
### Sync CLI commands
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# Interactive setup wizard
|
|
271
|
+
openclaw workspace-sync setup
|
|
272
|
+
|
|
273
|
+
# Check sync status
|
|
274
|
+
openclaw workspace-sync status
|
|
275
|
+
|
|
276
|
+
# Sync (behavior depends on mode)
|
|
277
|
+
openclaw workspace-sync sync
|
|
278
|
+
|
|
279
|
+
# Preview changes without syncing
|
|
280
|
+
openclaw workspace-sync sync --dry-run
|
|
281
|
+
|
|
282
|
+
# One-way sync (explicit, overrides mode for this run)
|
|
283
|
+
openclaw workspace-sync sync --direction pull # remote -> local
|
|
284
|
+
openclaw workspace-sync sync --direction push # local -> remote
|
|
285
|
+
|
|
286
|
+
# Force re-establish bisync baseline (bisync mode only)
|
|
287
|
+
openclaw workspace-sync sync --resync
|
|
288
|
+
|
|
289
|
+
# Authorize with cloud provider
|
|
290
|
+
openclaw workspace-sync authorize
|
|
291
|
+
openclaw workspace-sync authorize --provider gdrive
|
|
292
|
+
|
|
293
|
+
# List remote files
|
|
294
|
+
openclaw workspace-sync list
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Auto-sync
|
|
298
|
+
|
|
299
|
+
#### Session hooks
|
|
300
|
+
|
|
301
|
+
Sync automatically when sessions start or end. These run during existing agent activity and incur zero LLM cost:
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{
|
|
305
|
+
"onSessionStart": true,
|
|
306
|
+
"onSessionEnd": false
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### Periodic background sync
|
|
311
|
+
|
|
312
|
+
Set `interval` to enable automatic background sync (in seconds):
|
|
313
|
+
|
|
314
|
+
```json
|
|
315
|
+
{
|
|
316
|
+
"interval": 300,
|
|
317
|
+
"timeout": 3600
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The gateway runs sync in the background at this interval. Minimum interval is 60 seconds. The `timeout` controls how long each sync operation is allowed to run (default: 1800s / 30 min). Increase this for large workspaces or slow connections.
|
|
322
|
+
|
|
323
|
+
In `mailbox` mode, periodic sync pushes the workspace to the cloud and drains the `_outbox`. In `mirror` mode, periodic sync pulls the latest workspace state down to local. In `bisync` mode, it runs a full bidirectional sync.
|
|
324
|
+
|
|
325
|
+
#### External cron (alternative)
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
# Add to crontab (crontab -e)
|
|
329
|
+
*/5 * * * * openclaw workspace-sync sync >> /var/log/openclaw-sync.log 2>&1
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### Before your first sync
|
|
168
333
|
|
|
169
334
|
Getting the initial state right prevents data loss. Each mode has different requirements:
|
|
170
335
|
|
|
171
|
-
|
|
336
|
+
#### `mailbox` mode — starting state
|
|
172
337
|
|
|
173
338
|
The first sync **pushes** your local workspace to the cloud. This means rclone makes cloud match local exactly — any files on cloud that don't exist locally will be **deleted**.
|
|
174
339
|
|
|
@@ -185,17 +350,17 @@ rclone sync <remote>:<path> /data/workspace/ --config <config-path> \
|
|
|
185
350
|
|
|
186
351
|
Then verify local matches remote before enabling the plugin.
|
|
187
352
|
|
|
188
|
-
|
|
353
|
+
#### `mirror` mode — starting state
|
|
189
354
|
|
|
190
355
|
The first sync **pulls** from cloud to local. Local can be empty, stale, or corrupted — the pull simply overwrites it. **No preparation needed.**
|
|
191
356
|
|
|
192
|
-
|
|
357
|
+
#### `bisync` mode — starting state
|
|
193
358
|
|
|
194
359
|
The first sync requires `--resync`, which copies everything from both sides to the other. Any stale or unwanted files on either side will propagate.
|
|
195
360
|
|
|
196
361
|
**Recommended starting state:** Both sides are identical, or one side is empty and the other has the data you want. Verify both before running `--resync`.
|
|
197
362
|
|
|
198
|
-
|
|
363
|
+
#### General first-sync checklist
|
|
199
364
|
|
|
200
365
|
1. Run a `--dry-run` first to see what would happen: `openclaw workspace-sync sync --dry-run`
|
|
201
366
|
2. Check the output for unexpected deletions
|
|
@@ -204,104 +369,254 @@ The first sync requires `--resync`, which copies everything from both sides to t
|
|
|
204
369
|
|
|
205
370
|
> For maintenance, recovery, and common problems, see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
|
|
206
371
|
|
|
207
|
-
|
|
372
|
+
### Sync safety
|
|
373
|
+
|
|
374
|
+
Cloud sync involves two copies of your data. When things go wrong, one side can overwrite the other. Here is what to keep in mind:
|
|
208
375
|
|
|
209
|
-
|
|
376
|
+
**Mailbox mode is the safest.** The workspace pushes to the cloud; users send files via `_outbox`. The two streams never overlap. Even if your local folder is wiped, the next push re-creates everything. Even if the `_outbox` has stale files, they just land in `_inbox` for the agent to handle.
|
|
210
377
|
|
|
211
|
-
|
|
212
|
-
2. **Verify the remote** is accessible: `openclaw workspace-sync status`
|
|
213
|
-
3. **Run a dry-run first** to see what would happen: `openclaw workspace-sync sync --dry-run`
|
|
214
|
-
4. **Run the first sync**: `openclaw workspace-sync sync`
|
|
215
|
-
- In `mailbox` mode, this pushes the workspace and drains the `_outbox`
|
|
216
|
-
- In `mirror` mode, this pulls the current workspace down
|
|
217
|
-
- In `bisync` mode, this requires `--resync` to establish the baseline
|
|
218
|
-
5. **Enable periodic sync** by setting `interval` in your config
|
|
378
|
+
**Mirror mode is safe by design.** The remote workspace is the authority. Local is a read-only copy. Even if your local folder is empty, stale, or corrupted, the next sync just re-downloads the workspace. The agent's work is never affected by local state.
|
|
219
379
|
|
|
220
|
-
|
|
380
|
+
**Bisync requires both sides to agree.** Bisync tracks what changed since the last sync. If that tracking state is lost (deploy, disk wipe, moving to a new machine), rclone does not know what changed and requires a `--resync`. A resync copies everything from both sides — if one side has stale or unwanted files, they propagate to the other.
|
|
221
381
|
|
|
222
|
-
|
|
382
|
+
**Common pitfalls to avoid:**
|
|
383
|
+
- Changing `remotePath` or `localPath` while periodic sync is enabled
|
|
384
|
+
- Running `--resync` without checking both sides first
|
|
385
|
+
- Using `bisync` on container platforms where state is ephemeral
|
|
386
|
+
- Syncing very large directories (use `exclude` patterns liberally)
|
|
387
|
+
|
|
388
|
+
**If in doubt, use `mailbox` mode.** It gives you a live local mirror of the workspace and a clean way to send files to the agent, with no risk of data loss.
|
|
389
|
+
|
|
390
|
+
> For recovery procedures, mode switching, and maintenance tips, see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
|
|
391
|
+
|
|
392
|
+
#### Important: `--resync` is destructive (bisync only)
|
|
393
|
+
|
|
394
|
+
**Never use `--resync` unless you know exactly what it does.** The `--resync` flag tells rclone to throw away its knowledge of what has changed and do a full reconciliation — it copies every file that exists on either side to the other side. This means:
|
|
395
|
+
|
|
396
|
+
- Files you deleted remotely will come back from local (and vice versa)
|
|
397
|
+
- It transfers your **entire** sync scope, not just recent changes
|
|
398
|
+
- On a large Dropbox, this can take 30+ minutes and fill your disk
|
|
399
|
+
|
|
400
|
+
Normal bisync (without `--resync`) only transfers files that changed since the last sync. The plugin **never** auto-resyncs. If bisync's internal state gets corrupted, it will log a message telling you to run `--resync` manually — but only do this after confirming both sides are in the state you want.
|
|
223
401
|
|
|
224
402
|
```bash
|
|
225
|
-
|
|
403
|
+
# Only when you explicitly need to re-establish the baseline:
|
|
404
|
+
openclaw workspace-sync sync --resync
|
|
226
405
|
```
|
|
227
406
|
|
|
228
|
-
|
|
407
|
+
### Sync troubleshooting
|
|
408
|
+
|
|
409
|
+
#### Token expired
|
|
229
410
|
|
|
230
411
|
```bash
|
|
231
|
-
|
|
232
|
-
git clone https://github.com/ashbrener/openclaw-workspace-sync workspace-sync
|
|
233
|
-
cd workspace-sync && npm install --omit=dev
|
|
412
|
+
openclaw workspace-sync authorize
|
|
234
413
|
```
|
|
235
414
|
|
|
236
|
-
|
|
415
|
+
#### Conflicts (bisync only)
|
|
416
|
+
|
|
417
|
+
Files modified on both sides get a `.conflict` suffix. The winner is determined by `conflictResolve` (default: `newer`). To find conflict files:
|
|
237
418
|
|
|
238
419
|
```bash
|
|
239
|
-
|
|
240
|
-
openclaw workspace-sync setup
|
|
420
|
+
find <workspace>/shared -name "*.conflict"
|
|
241
421
|
```
|
|
242
422
|
|
|
243
|
-
|
|
244
|
-
1. Checking/installing rclone
|
|
245
|
-
2. Selecting cloud provider
|
|
246
|
-
3. Choosing sync mode
|
|
247
|
-
4. Dropbox app folder option (for scoped access)
|
|
248
|
-
5. Background sync interval
|
|
249
|
-
6. OAuth authorization
|
|
250
|
-
7. First sync
|
|
423
|
+
#### Stale lock files
|
|
251
424
|
|
|
252
|
-
|
|
425
|
+
The plugin automatically handles stale rclone lock files. If a sync is interrupted (timeout, crash, kill), the next run detects the stale lock, clears it, and retries. Lock files older than 15 minutes are treated as expired by rclone's `--max-lock` flag.
|
|
253
426
|
|
|
254
|
-
|
|
427
|
+
If you still see lock errors, you can manually clear them:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
rclone deletefile ~/.cache/rclone/bisync/<lockfile>.lck
|
|
431
|
+
```
|
|
255
432
|
|
|
256
|
-
|
|
433
|
+
#### Sync times out
|
|
434
|
+
|
|
435
|
+
Increase the `timeout` config (in seconds). The default is 1800 (30 min). For large workspaces:
|
|
257
436
|
|
|
258
437
|
```json
|
|
259
438
|
{
|
|
260
|
-
"
|
|
261
|
-
"entries": {
|
|
262
|
-
"openclaw-workspace-sync": {
|
|
263
|
-
"enabled": true,
|
|
264
|
-
"config": {
|
|
265
|
-
"provider": "dropbox",
|
|
266
|
-
"mode": "mailbox",
|
|
267
|
-
"remotePath": "",
|
|
268
|
-
"localPath": "/",
|
|
269
|
-
"interval": 60,
|
|
270
|
-
"timeout": 1800,
|
|
271
|
-
"onSessionStart": true,
|
|
272
|
-
"onSessionEnd": false,
|
|
273
|
-
"exclude": [".git/**", "node_modules/**", "*.log"]
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
439
|
+
"timeout": 3600
|
|
278
440
|
}
|
|
279
441
|
```
|
|
280
442
|
|
|
281
|
-
|
|
443
|
+
#### Permission errors
|
|
444
|
+
|
|
445
|
+
```bash
|
|
446
|
+
chmod -R 755 <workspace>/shared
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
#### Dropbox rate limiting
|
|
450
|
+
|
|
451
|
+
Dropbox enforces API rate limits (`too_many_requests`). If your workspace has many files (10k+), each sync cycle can consume a large number of API calls just for checking. To avoid hitting limits:
|
|
452
|
+
|
|
453
|
+
- **Set `interval` high enough** for the sync to complete between cycles. A workspace with ~40k files takes ~2 minutes to scan. An `interval` of 180 (3 min) is the minimum; 300 (5 min) is safer.
|
|
454
|
+
- **Use `exclude` patterns liberally** — skip `node_modules`, `.git`, `__pycache__`, build output, and anything you don't need synced. Fewer files = fewer API calls.
|
|
455
|
+
- **If you see `too_many_requests` errors** in the logs, increase the interval and add more excludes.
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Encrypted backups
|
|
460
|
+
|
|
461
|
+
Back up your entire agent system — workspace, config, cron jobs, memory, sessions — as encrypted snapshots to your own cloud storage. Your bucket, your encryption key, zero monthly fees.
|
|
462
|
+
|
|
463
|
+
### How it works
|
|
464
|
+
|
|
465
|
+
```mermaid
|
|
466
|
+
flowchart TD
|
|
467
|
+
subgraph GW["Gateway"]
|
|
468
|
+
WS["/workspace"]
|
|
469
|
+
CFG["config / cron / memory"]
|
|
470
|
+
end
|
|
471
|
+
TAR["tar cz"]
|
|
472
|
+
ENC["openssl enc\n(AES-256)"]
|
|
473
|
+
RCAT["rclone rcat"]
|
|
474
|
+
subgraph REMOTE["Your Bucket (S3 / R2 / B2)"]
|
|
475
|
+
SNAP["backup-2026…Z.tar.gz.enc"]
|
|
476
|
+
OLD["older snapshots"]
|
|
477
|
+
end
|
|
478
|
+
WS --> TAR
|
|
479
|
+
CFG --> TAR
|
|
480
|
+
TAR -- "pipe" --> ENC
|
|
481
|
+
ENC -- "pipe" --> RCAT
|
|
482
|
+
RCAT --> SNAP
|
|
483
|
+
SNAP -. "retention prune" .-> OLD
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
1. **Streams directly** — `tar | [openssl enc] | rclone rcat` piped straight to the remote. Zero local temp files, zero extra disk needed. A 10 GB workspace on a 1 GB free volume? No problem.
|
|
487
|
+
2. Optionally encrypts with AES-256 (client-side, before upload) via `openssl`
|
|
488
|
+
3. Uploads via rclone to any supported provider (S3, R2, Backblaze B2, Dropbox, etc.)
|
|
489
|
+
4. Prunes old snapshots based on your retention policy
|
|
490
|
+
|
|
491
|
+
> **Disk-constrained?** Because backups stream directly, you don't need any free disk space for the backup itself. Only the restore downloads to a staging directory.
|
|
492
|
+
|
|
493
|
+
### Backup config reference
|
|
282
494
|
|
|
283
495
|
| Key | Type | Default | Description |
|
|
284
496
|
|-----|------|---------|-------------|
|
|
285
|
-
| `
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
291
|
-
| `
|
|
292
|
-
| `
|
|
293
|
-
| `
|
|
294
|
-
| `
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
|
299
|
-
|
|
300
|
-
| `
|
|
497
|
+
| `enabled` | boolean | `false` | Enable scheduled backups |
|
|
498
|
+
| `provider` | string | parent provider | Cloud provider for backup storage |
|
|
499
|
+
| `bucket` | string | — | S3/R2 bucket name |
|
|
500
|
+
| `prefix` | string | `""` | Path prefix within the bucket (e.g., `habibi/`) |
|
|
501
|
+
| `interval` | number | `86400` | Backup interval in seconds (86400 = daily; clamped to min 300) |
|
|
502
|
+
| `encrypt` | boolean | `false` | Encrypt snapshots with AES-256 before upload |
|
|
503
|
+
| `passphrase` | string | — | Encryption passphrase (use `${BACKUP_PASSPHRASE}` env var) |
|
|
504
|
+
| `include` | string[] | see below | What to back up |
|
|
505
|
+
| `retain` | number or object | `7` | Retention: `7` = keep 7 latest, or `{ daily: 7, weekly: 4 }` |
|
|
506
|
+
| `exclude` | string[] | parent excludes | Glob patterns to exclude from workspace backup |
|
|
507
|
+
|
|
508
|
+
### Include options
|
|
509
|
+
|
|
510
|
+
| Item | What gets backed up |
|
|
511
|
+
|------|---------------------|
|
|
512
|
+
| `workspace` | The workspace directory (agent files, projects, etc.) |
|
|
513
|
+
| `config` | `openclaw.json` |
|
|
514
|
+
| `cron` | Cron job schedules and state |
|
|
515
|
+
| `memory` | Memory files (MEMORY.md, etc.) |
|
|
516
|
+
| `sessions` | Session metadata and store |
|
|
517
|
+
| `credentials` | Auth profile credentials |
|
|
518
|
+
| `skills` | Skill files |
|
|
519
|
+
| `hooks` | Webhook configurations and state (Gmail watch, custom hooks) |
|
|
520
|
+
| `extensions` | Installed plugins/extensions (for reproducible restores) |
|
|
521
|
+
| `env` | Environment variables file (`.env`) |
|
|
522
|
+
| `agents` | Multi-agent state (per-agent sessions, subagent registry) |
|
|
523
|
+
| `pages` | Custom pages served by the gateway |
|
|
524
|
+
| `transcripts` | Full conversation logs (JSONL session transcripts) |
|
|
525
|
+
|
|
526
|
+
Default: `["workspace", "config", "cron", "memory"]`
|
|
527
|
+
|
|
528
|
+
### Backup CLI commands
|
|
301
529
|
|
|
302
|
-
|
|
530
|
+
```bash
|
|
531
|
+
# Create a backup now
|
|
532
|
+
openclaw workspace-sync backup now
|
|
533
|
+
|
|
534
|
+
# List available snapshots
|
|
535
|
+
openclaw workspace-sync backup list
|
|
536
|
+
|
|
537
|
+
# Restore the latest snapshot
|
|
538
|
+
openclaw workspace-sync backup restore
|
|
539
|
+
|
|
540
|
+
# Restore a specific snapshot
|
|
541
|
+
openclaw workspace-sync backup restore --snapshot backup-20260310T020000Z.tar.gz.enc
|
|
542
|
+
|
|
543
|
+
# Restore to a specific directory (safe — doesn't overwrite live data)
|
|
544
|
+
openclaw workspace-sync backup restore --to /tmp/restore-test
|
|
545
|
+
|
|
546
|
+
# Check backup service status
|
|
547
|
+
openclaw workspace-sync backup status
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Restore safety
|
|
551
|
+
|
|
552
|
+
By default, `restore` extracts to a staging directory (`~/.openclaw/.backup-restore/`), not directly over your live workspace. This lets you inspect the contents before copying them into place. Use `--to` to control where files land.
|
|
553
|
+
|
|
554
|
+
### Backup provider examples
|
|
555
|
+
|
|
556
|
+
**Cloudflare R2 (free tier: 10GB):**
|
|
557
|
+
|
|
558
|
+
```json
|
|
559
|
+
{
|
|
560
|
+
"backup": {
|
|
561
|
+
"enabled": true,
|
|
562
|
+
"provider": "s3",
|
|
563
|
+
"encrypt": true,
|
|
564
|
+
"passphrase": "${BACKUP_PASSPHRASE}",
|
|
565
|
+
"s3": {
|
|
566
|
+
"endpoint": "https://<account-id>.r2.cloudflarestorage.com",
|
|
567
|
+
"bucket": "openclaw-backups",
|
|
568
|
+
"region": "auto",
|
|
569
|
+
"accessKeyId": "${R2_ACCESS_KEY}",
|
|
570
|
+
"secretAccessKey": "${R2_SECRET_KEY}"
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
**AWS S3:**
|
|
577
|
+
|
|
578
|
+
```json
|
|
579
|
+
{
|
|
580
|
+
"backup": {
|
|
581
|
+
"enabled": true,
|
|
582
|
+
"provider": "s3",
|
|
583
|
+
"encrypt": true,
|
|
584
|
+
"passphrase": "${BACKUP_PASSPHRASE}",
|
|
585
|
+
"s3": {
|
|
586
|
+
"bucket": "my-openclaw-backups",
|
|
587
|
+
"region": "us-east-1",
|
|
588
|
+
"accessKeyId": "${AWS_ACCESS_KEY_ID}",
|
|
589
|
+
"secretAccessKey": "${AWS_SECRET_ACCESS_KEY}"
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Backblaze B2:**
|
|
596
|
+
|
|
597
|
+
```json
|
|
598
|
+
{
|
|
599
|
+
"backup": {
|
|
600
|
+
"enabled": true,
|
|
601
|
+
"provider": "s3",
|
|
602
|
+
"encrypt": true,
|
|
603
|
+
"passphrase": "${BACKUP_PASSPHRASE}",
|
|
604
|
+
"s3": {
|
|
605
|
+
"endpoint": "https://s3.us-west-002.backblazeb2.com",
|
|
606
|
+
"bucket": "openclaw-backups",
|
|
607
|
+
"region": "us-west-002",
|
|
608
|
+
"accessKeyId": "${B2_KEY_ID}",
|
|
609
|
+
"secretAccessKey": "${B2_APP_KEY}"
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
```
|
|
303
614
|
|
|
304
|
-
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Provider-specific options
|
|
618
|
+
|
|
619
|
+
These provider blocks work for both sync and backup. Place them inside the `sync` or `backup` config block as needed.
|
|
305
620
|
|
|
306
621
|
**Dropbox with app folder (recommended for security):**
|
|
307
622
|
|
|
@@ -387,72 +702,7 @@ With `appFolder: true`, set `remotePath` to `""`. The app folder root is your sy
|
|
|
387
702
|
|
|
388
703
|
The `custom` provider accepts any [rclone backend type](https://rclone.org/overview/) and passes `rcloneOptions` directly to the rclone config. This gives you config-driven access to all 70+ providers without manually editing `rclone.conf`.
|
|
389
704
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
```bash
|
|
393
|
-
# Interactive setup wizard
|
|
394
|
-
openclaw workspace-sync setup
|
|
395
|
-
|
|
396
|
-
# Check sync status
|
|
397
|
-
openclaw workspace-sync status
|
|
398
|
-
|
|
399
|
-
# Sync (behavior depends on mode)
|
|
400
|
-
openclaw workspace-sync sync
|
|
401
|
-
|
|
402
|
-
# Preview changes without syncing
|
|
403
|
-
openclaw workspace-sync sync --dry-run
|
|
404
|
-
|
|
405
|
-
# One-way sync (explicit, overrides mode for this run)
|
|
406
|
-
openclaw workspace-sync sync --direction pull # remote -> local
|
|
407
|
-
openclaw workspace-sync sync --direction push # local -> remote
|
|
408
|
-
|
|
409
|
-
# Force re-establish bisync baseline (bisync mode only)
|
|
410
|
-
openclaw workspace-sync sync --resync
|
|
411
|
-
|
|
412
|
-
# Authorize with cloud provider
|
|
413
|
-
openclaw workspace-sync authorize
|
|
414
|
-
openclaw workspace-sync authorize --provider gdrive
|
|
415
|
-
|
|
416
|
-
# List remote files
|
|
417
|
-
openclaw workspace-sync list
|
|
418
|
-
```
|
|
419
|
-
|
|
420
|
-
## Auto-sync
|
|
421
|
-
|
|
422
|
-
### Session hooks
|
|
423
|
-
|
|
424
|
-
Sync automatically when sessions start or end. These run during existing agent activity and incur zero LLM cost:
|
|
425
|
-
|
|
426
|
-
```json
|
|
427
|
-
{
|
|
428
|
-
"onSessionStart": true,
|
|
429
|
-
"onSessionEnd": false
|
|
430
|
-
}
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### Periodic background sync
|
|
434
|
-
|
|
435
|
-
Set `interval` to enable automatic background sync (in seconds):
|
|
436
|
-
|
|
437
|
-
```json
|
|
438
|
-
{
|
|
439
|
-
"interval": 300,
|
|
440
|
-
"timeout": 3600
|
|
441
|
-
}
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
The gateway runs sync in the background at this interval. Minimum interval is 60 seconds. The `timeout` controls how long each sync operation is allowed to run (default: 1800s / 30 min). Increase this for large workspaces or slow connections.
|
|
445
|
-
|
|
446
|
-
In `mailbox` mode, periodic sync pushes the workspace to the cloud and drains the `_outbox`. In `mirror` mode, periodic sync pulls the latest workspace state down to local. In `bisync` mode, it runs a full bidirectional sync.
|
|
447
|
-
|
|
448
|
-
### External cron (alternative)
|
|
449
|
-
|
|
450
|
-
```bash
|
|
451
|
-
# Add to crontab (crontab -e)
|
|
452
|
-
*/5 * * * * openclaw workspace-sync sync >> /var/log/openclaw-sync.log 2>&1
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
## Supported providers
|
|
705
|
+
### Supported providers
|
|
456
706
|
|
|
457
707
|
| Provider | Config value | Auth method | Config-driven |
|
|
458
708
|
|----------|-------------|-------------|---------------|
|
|
@@ -464,24 +714,7 @@ In `mailbox` mode, periodic sync pushes the workspace to the cloud and drains th
|
|
|
464
714
|
|
|
465
715
|
All providers are fully config-driven — no manual `rclone.conf` editing needed. The `custom` provider gives access to all [70+ rclone backends](https://rclone.org/overview/) (SFTP, B2, Mega, pCloud, Azure Blob, etc.).
|
|
466
716
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
If you prefer to skip the interactive wizard, configure the plugin in `openclaw.json` and use the CLI:
|
|
470
|
-
|
|
471
|
-
```bash
|
|
472
|
-
# 1. Authorize with your cloud provider
|
|
473
|
-
openclaw workspace-sync authorize --provider dropbox
|
|
474
|
-
|
|
475
|
-
# 2. Run a dry-run to preview what will sync
|
|
476
|
-
openclaw workspace-sync sync --dry-run
|
|
477
|
-
|
|
478
|
-
# 3. Run the first sync
|
|
479
|
-
openclaw workspace-sync sync
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
The plugin handles rclone installation, config generation, and token storage automatically based on your `openclaw.json` settings.
|
|
483
|
-
|
|
484
|
-
## Dropbox app folder access (recommended)
|
|
717
|
+
### Dropbox app folder access (recommended)
|
|
485
718
|
|
|
486
719
|
For better security, create a scoped Dropbox app that only accesses a single folder:
|
|
487
720
|
|
|
@@ -502,90 +735,7 @@ Benefits:
|
|
|
502
735
|
- If token is compromised, blast radius is limited
|
|
503
736
|
- Clean separation — sync folder lives under `Apps/<your-app-name>/`
|
|
504
737
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
Dropbox enforces API rate limits (`too_many_requests`). If your workspace has many files (10k+), each sync cycle can consume a large number of API calls just for checking. To avoid hitting limits:
|
|
508
|
-
|
|
509
|
-
- **Set `interval` high enough** for the sync to complete between cycles. A workspace with ~40k files takes ~2 minutes to scan. An `interval` of 180 (3 min) is the minimum; 300 (5 min) is safer.
|
|
510
|
-
- **Use `exclude` patterns liberally** — skip `node_modules`, `.git`, `__pycache__`, build output, and anything you don't need synced. Fewer files = fewer API calls.
|
|
511
|
-
- **If you see `too_many_requests` errors** in the logs, increase the interval and add more excludes.
|
|
512
|
-
|
|
513
|
-
## Understanding sync safety
|
|
514
|
-
|
|
515
|
-
Cloud sync involves two copies of your data. When things go wrong, one side can overwrite the other. Here is what to keep in mind:
|
|
516
|
-
|
|
517
|
-
**Mailbox mode is the safest.** The workspace pushes to the cloud; users send files via `_outbox`. The two streams never overlap. Even if your local folder is wiped, the next push re-creates everything. Even if the `_outbox` has stale files, they just land in `_inbox` for the agent to handle.
|
|
518
|
-
|
|
519
|
-
**Mirror mode is safe by design.** The remote workspace is the authority. Local is a read-only copy. Even if your local folder is empty, stale, or corrupted, the next sync just re-downloads the workspace. The agent's work is never affected by local state.
|
|
520
|
-
|
|
521
|
-
**Bisync requires both sides to agree.** Bisync tracks what changed since the last sync. If that tracking state is lost (deploy, disk wipe, moving to a new machine), rclone does not know what changed and requires a `--resync`. A resync copies everything from both sides — if one side has stale or unwanted files, they propagate to the other.
|
|
522
|
-
|
|
523
|
-
**Common pitfalls to avoid:**
|
|
524
|
-
- Changing `remotePath` or `localPath` while periodic sync is enabled
|
|
525
|
-
- Running `--resync` without checking both sides first
|
|
526
|
-
- Using `bisync` on container platforms where state is ephemeral
|
|
527
|
-
- Syncing very large directories (use `exclude` patterns liberally)
|
|
528
|
-
|
|
529
|
-
**If in doubt, use `mailbox` mode.** It gives you a live local mirror of the workspace and a clean way to send files to the agent, with no risk of data loss.
|
|
530
|
-
|
|
531
|
-
> For recovery procedures, mode switching, and maintenance tips, see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
|
|
532
|
-
|
|
533
|
-
## Important: `--resync` is destructive (bisync only)
|
|
534
|
-
|
|
535
|
-
**Never use `--resync` unless you know exactly what it does.** The `--resync` flag tells rclone to throw away its knowledge of what has changed and do a full reconciliation — it copies every file that exists on either side to the other side. This means:
|
|
536
|
-
|
|
537
|
-
- Files you deleted remotely will come back from local (and vice versa)
|
|
538
|
-
- It transfers your **entire** sync scope, not just recent changes
|
|
539
|
-
- On a large Dropbox, this can take 30+ minutes and fill your disk
|
|
540
|
-
|
|
541
|
-
Normal bisync (without `--resync`) only transfers files that changed since the last sync. The plugin **never** auto-resyncs. If bisync's internal state gets corrupted, it will log a message telling you to run `--resync` manually — but only do this after confirming both sides are in the state you want.
|
|
542
|
-
|
|
543
|
-
```bash
|
|
544
|
-
# Only when you explicitly need to re-establish the baseline:
|
|
545
|
-
openclaw workspace-sync sync --resync
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
## Troubleshooting
|
|
549
|
-
|
|
550
|
-
### Token expired
|
|
551
|
-
|
|
552
|
-
```bash
|
|
553
|
-
openclaw workspace-sync authorize
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
### Conflicts (bisync only)
|
|
557
|
-
|
|
558
|
-
Files modified on both sides get a `.conflict` suffix. The winner is determined by `conflictResolve` (default: `newer`). To find conflict files:
|
|
559
|
-
|
|
560
|
-
```bash
|
|
561
|
-
find <workspace>/shared -name "*.conflict"
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
### Stale lock files
|
|
565
|
-
|
|
566
|
-
The plugin automatically handles stale rclone lock files. If a sync is interrupted (timeout, crash, kill), the next run detects the stale lock, clears it, and retries. Lock files older than 15 minutes are treated as expired by rclone's `--max-lock` flag.
|
|
567
|
-
|
|
568
|
-
If you still see lock errors, you can manually clear them:
|
|
569
|
-
|
|
570
|
-
```bash
|
|
571
|
-
rclone deletefile ~/.cache/rclone/bisync/<lockfile>.lck
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
### Sync times out
|
|
575
|
-
|
|
576
|
-
Increase the `timeout` config (in seconds). The default is 1800 (30 min). For large workspaces:
|
|
577
|
-
|
|
578
|
-
```json
|
|
579
|
-
{
|
|
580
|
-
"timeout": 3600
|
|
581
|
-
}
|
|
582
|
-
```
|
|
583
|
-
|
|
584
|
-
### Permission errors
|
|
585
|
-
|
|
586
|
-
```bash
|
|
587
|
-
chmod -R 755 <workspace>/shared
|
|
588
|
-
```
|
|
738
|
+
---
|
|
589
739
|
|
|
590
740
|
## Deployment recommendations
|
|
591
741
|
|
|
@@ -594,15 +744,15 @@ If you are running OpenClaw on a cloud container (Fly.io, Railway, Render) or a
|
|
|
594
744
|
- **Use a separate persistent volume for the workspace.** Container root filesystems are ephemeral — a redeploy wipes everything. Mount a dedicated volume (e.g., Fly.io volumes, EBS, DigitalOcean block storage) at your workspace path so data survives deploys and restarts.
|
|
595
745
|
- **Enable daily volume snapshots.** Most cloud providers offer automated snapshots (Fly.io does this by default with 5-day retention). If something goes wrong — a bad sync, accidental deletion, or a failed reorganization — a recent snapshot lets you restore in minutes instead of rebuilding from scratch.
|
|
596
746
|
- **Test your restore process.** A backup you have never restored is a backup you do not have. Create a volume from a snapshot at least once to confirm the process works and you know the steps.
|
|
597
|
-
|
|
598
|
-
These recommendations apply regardless of whether you use this plugin. Cloud sync adds convenience but is not a substitute for proper backups.
|
|
747
|
+
- **Use encrypted backups for off-site disaster recovery.** Volume snapshots protect against accidental deletes, but not against provider outages or account issues. Streaming encrypted backups to a separate provider (e.g., R2, B2) gives you a fully independent recovery path.
|
|
599
748
|
|
|
600
749
|
## Security notes
|
|
601
750
|
|
|
602
751
|
- **Token storage**: rclone tokens are stored in `rclone.conf` with `0600` permissions
|
|
603
752
|
- **Sensitive files**: Don't sync secrets, API keys, or credentials
|
|
604
|
-
- **Encryption**:
|
|
753
|
+
- **Encryption**: Backups support AES-256 client-side encryption. For sync, consider [rclone crypt](https://rclone.org/crypt/) for sensitive data.
|
|
605
754
|
- **App folder**: Use Dropbox app folder access for minimal permissions
|
|
755
|
+
- **Passphrase safety**: Use environment variables (`${BACKUP_PASSPHRASE}`) — never hardcode passphrases in config files
|
|
606
756
|
|
|
607
757
|
## Development
|
|
608
758
|
|