{"id":"837b7c45-c0e0-4b08-af5b-571e4425e2a0","entityType":"agent","slug":"clawhub-obuchowski-openclaw-cloud-backup","name":"Cloud Backup [S3, R2, B2, MinIO & more]","canonicalUrl":"https://www.xpersona.co/agent/clawhub-obuchowski-openclaw-cloud-backup","canonicalPath":"/agent/clawhub-obuchowski-openclaw-cloud-backup","generatedAt":"2026-10-11T21:50:04.817Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":null},"description":"Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw.","descriptionLabel":"Source description","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1K downloads reported by the source. Last updated 10/11/2026.","installCommand":"clawhub skill install s1761zhqb0c4es4qj1m36xxw1x83ypeq:openclaw-cloud-backup","sourceUrl":"https://clawhub.ai/obuchowski/openclaw-cloud-backup","homepage":"https://clawhub.ai/obuchowski/skills/openclaw-cloud-backup","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/obuchowski/openclaw-cloud-backup","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/obuchowski/skills/openclaw-cloud-backup","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":60,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Cloud Backup [S3, R2, B2, MinIO & more] technical dossier on Xpersona with agent coverage, OPENCLEW support, and live trust metadata."},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":null},"stars":null,"forks":null,"downloads":1008,"likes":null,"task":null,"library":null,"packageName":null,"latestVersion":"1.1.4","tractionLabel":"1K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T18:42:40.904Z","emptyReason":null},"lastUpdatedAt":"2026-10-11T18:42:40.919Z","lastCrawledAt":"2026-10-11T18:42:40.904Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-12T18:42:40.904Z","lastVerifiedAt":null,"highlights":[{"version":"1.1.4","createdAt":"2026-07-02T13:45:02.362Z","changelog":"## 2.0.2 — 2026-07-02 ### Changed - Lean filter now excludes `agents/*/qmd/xdg-cache/**` (qmd embedding caches: downloaded GGUF models and `index.sqlite`). Both are rebuildable from the indexed sources and had inflated full archives from ~170 MB to ~1.17 GB once qmd started caching local embedding models under agent state.","fileCount":15,"zipByteSize":46522},{"version":"1.1.3","createdAt":"2026-06-24T16:44:32.703Z","changelog":"Re-publish to re-trigger review (1.1.2 stuck in llm_review); no functional changes.","fileCount":15,"zipByteSize":46337},{"version":"1.1.2","createdAt":"2026-06-11T22:39:45.254Z","changelog":"Description variant (A/B test against the cloud-backup listing). Same content as cloud-backup 2.0.1 otherwise.","fileCount":15,"zipByteSize":46250},{"version":"1.1.1","createdAt":"2026-06-11T22:18:50.326Z","changelog":"Display-name restore only; identical content to 1.1.0 (rebuilt on the cloud-backup v2.0.0 codebase).","fileCount":15,"zipByteSize":46196},{"version":"1.1.0","createdAt":"2026-06-11T22:11:21.447Z","changelog":"Rebuilt on the cloud-backup v2.0.0 codebase — \"the cloud layer for OpenClaw's native backup\". ## 2.0.0 — 2026-06-11 Complete redesign: the skill is now **the cloud layer for OpenClaw's native backup**, and was rebuilt around the ClawHub security-scan findings of v1.1.5 (see SECURITY.md for the point-by-point map). ### Breaking - The `full` engine is `openclaw backup create` (consistent SQLite snapshots, embedded manifest, multi-directory coverage) plus a lean filter. The `openclaw` CLI is therefore required for `backup full`. - `full` is **lean by default**: session transcripts, Codex caches/log databases, `tools/`, `media/`, `logs/`, and old backups are excluded. `--everything` (or `config.everything=true`) restores the old behavior. - `config.encrypt` now defaults to **true**, and encryption is **forced** (exit 14 rather than a warning) whenever the archive scope contains real secret material — detected automatically. - The local archive store moves from `~/.openclaw/backups` to `~/.local/share/openclaw-backups` (`config.localDir`). Required: the native engine refuses output inside a source path, and v1's location made every new full backup swallow all previous ones. - `cleanup` renamed to `prune`; mode `skills` folded into `workspace` (both aliases still work, with warnings). - Restores are **staged by default** (new directory + printed next steps); in-place restores moved behind `--in-place` with typed confirmation (`--yes --force` for automation). ### Security (ClawHub scan remediation) - **SQP-1**: activation narrowed to explicit OpenClaw-backup intent; SKILL.md adds per-action confirmation gates (config writes, first upload, credential storage, restore, prune, schedule creation) and an unattended-run policy. - **SQP-2 (cron)**: the daily job is no longer created by default. Scheduling is opt-in, offered once after the first successful manual backup, with the exact `openclaw cron add` command and payload shown beforehand. The new `schedule` subcommand only prints. - **SQP-2 (credentials)**: all provider docs now lead with least-privilege bucket-scoped keys in AWS named profiles or operator-managed env; the GPG passphrase moves to a chmod-600 passphrase file or an OpenClaw SecretRef (`apiKey` injected as `CLOUD_BACKUP_GPG_PASSPHRASE`); the passphrase is passed to gpg via file descriptor, never argv; plaintext `skills.entries.cloud-backup.env.*` still resolves but warns loudly on every run. - Durable plaintext leftovers are structurally impossible: archives are built in a per-run mode-700 staging dir, removed on any exit, swept after hard kills. (v1 stranded unencrypted tarballs whenever a step failed between tar and gpg.) ### Added - **OpenClaw secret-store integration**: `config.accessKeyRef` / `secretKeyRef` / `sessionTokenRef` / `passphraseRef` accept OpenClaw SecretRefs (`{source: env|file|exec, provider, id}` or `$NAME` templates), resolved against the instance's `.secrets.providers` with the gateway's own semantics (JSON-pointer file stores, env vars, protocolVersion-1 exec backends such as 1Password wrappers). Configured-but-unresolvable refs abort the run (exit 13/14) and show as `UNRESOLVABLE` in `status` — never a silent fallback to a weaker tier. - `verify [name|--latest] [--deep]` — checksum + decrypt + listing; `--deep` also runs the native `openclaw backup verify` on the decrypted archive. - Automatic post-backup verification (`config.verifyAfterBackup`, default `quick`: streamed decrypt + entry-count match) and post-upload HEAD verification (size + sha256 metadata). - Sensitivity verdict (`refs-only` vs `secret-material`) shown in `status` and `--dry-run`, driving the forced-encryption policy; `config.excludeSecrets` to produce secret-free archives instead. - `config.exclude` / `config.include` (state-relative globs), `--no-upload`, `--json`, `backup --dry-run` plan output. - `prune --dry-run` with exact delete plan; failure-debris detection (plaintext leftovers, incomplete sets) in `list`/`status`/`prune`. - Per-mode retention (count + days) — a daily `settings` run can no longer evict your `full` archives. - `flock` concurrency lock, disk-space and cloud-reachability preflights, documented exit-code map for cron agents. - `restore --target DIR` (staged), `--only GLOB` (selective), `--latest`; v1 archives remain fully restorable. - New references: `credentials.md` (resolution chain + migration), `setup-flow.md` (guided, gated first-time setup). ### Deprecated (removed in v3) - `skills.entries.cloud-backup.env.ACCESS_KEY_ID` / `SECRET_ACCESS_KEY` / `SESSION_TOKEN` / `GPG_PASSPHRASE` — still work, warn on every run. - Command `cleanup`, mode `skills`.","fileCount":15,"zipByteSize":46137},{"version":"1.0.0","createdAt":"2026-02-17T14:59:22.743Z","changelog":"Initial release of OpenClaw Cloud Backup. - Back up and restore OpenClaw configuration locally or to S3-compatible cloud storage (AWS S3, Cloudflare R2, Backblaze B2, MinIO, DigitalOcean Spaces). - Provides commands for backup, restore, list, and cleanup operations. - Supports automated daily backup scheduling with OpenClaw's native cron. - Includes reference docs for provider setup, security, and local config. - Credentials managed securely via OpenClaw config with support for agent-assisted or manual setup. - Focus on security: least-privilege, secret protection, dry-run restore, and archive path validation.","fileCount":7,"zipByteSize":15293}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s1761zhqb0c4es4qj1m36xxw1x83ypeq:openclaw-cloud-backup","setupComplexity":"low","setupSteps":["Install using `clawhub skill install s1761zhqb0c4es4qj1m36xxw1x83ypeq:openclaw-cloud-backup` in an isolated environment before connecting it to live workloads.","No published capability contract is available yet, so validate auth and request/response behavior manually.","Review the upstream CLAWHUB listing at https://clawhub.ai/obuchowski/openclaw-cloud-backup before using production credentials."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-11T21:50:04.809Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-obuchowski-openclaw-cloud-backup/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":null},"readme":"Skill: Cloud Backup [S3, R2, B2, MinIO & more]\n\nOwner: obuchowski\n\nSummary: Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw.\n\nTags: latest:1.1.4\n\nVersion history:\n\nv1.1.4 | 2026-07-02T13:45:02.362Z | user\n\n## 2.0.2 — 2026-07-02\n\n### Changed\n\n- Lean filter now excludes `agents/*/qmd/xdg-cache/**` (qmd embedding caches:\n  downloaded GGUF models and `index.sqlite`). Both are rebuildable from the\n  indexed sources and had inflated full archives from ~170 MB to ~1.17 GB once\n  qmd started caching local embedding models under agent state.\n\nv1.1.3 | 2026-06-24T16:44:32.703Z | user\n\nRe-publish to re-trigger review (1.1.2 stuck in llm_review); no functional changes.\n\nv1.1.2 | 2026-06-11T22:39:45.254Z | user\n\nDescription variant (A/B test against the cloud-backup listing). Same content as cloud-backup 2.0.1 otherwise.\n\nv1.1.1 | 2026-06-11T22:18:50.326Z | user\n\nDisplay-name restore only; identical content to 1.1.0 (rebuilt on the cloud-backup v2.0.0 codebase).\n\nv1.1.0 | 2026-06-11T22:11:21.447Z | user\n\nRebuilt on the cloud-backup v2.0.0 codebase — \"the cloud layer for OpenClaw's native backup\".\n\n## 2.0.0 — 2026-06-11\n\nComplete redesign: the skill is now **the cloud layer for OpenClaw's native\nbackup**, and was rebuilt around the ClawHub security-scan findings of v1.1.5\n(see SECURITY.md for the point-by-point map).\n\n### Breaking\n\n- The `full` engine is `openclaw backup create` (consistent SQLite snapshots,\n  embedded manifest, multi-directory coverage) plus a lean filter. The\n  `openclaw` CLI is therefore required for `backup full`.\n- `full` is **lean by default**: session transcripts, Codex caches/log\n  databases, `tools/`, `media/`, `logs/`, and old backups are excluded.\n  `--everything` (or `config.everything=true`) restores the old behavior.\n- `config.encrypt` now defaults to **true**, and encryption is **forced**\n  (exit 14 rather than a warning) whenever the archive scope contains real\n  secret material — detected automatically.\n- The local archive store moves from `~/.openclaw/backups` to\n  `~/.local/share/openclaw-backups` (`config.localDir`). Required: the native\n  engine refuses output inside a source path, and v1's location made every\n  new full backup swallow all previous ones.\n- `cleanup` renamed to `prune`; mode `skills` folded into `workspace` (both\n  aliases still work, with warnings).\n- Restores are **staged by default** (new directory + printed next steps);\n  in-place restores moved behind `--in-place` with typed confirmation\n  (`--yes --force` for automation).\n\n### Security (ClawHub scan remediation)\n\n- **SQP-1**: activation narrowed to explicit OpenClaw-backup intent;\n  SKILL.md adds per-action confirmation gates (config writes, first upload,\n  credential storage, restore, prune, schedule creation) and an\n  unattended-run policy.\n- **SQP-2 (cron)**: the daily job is no longer created by default.\n  Scheduling is opt-in, offered once after the first successful manual\n  backup, with the exact `openclaw cron add` command and payload shown\n  beforehand. The new `schedule` subcommand only prints.\n- **SQP-2 (credentials)**: all provider docs now lead with least-privilege\n  bucket-scoped keys in AWS named profiles or operator-managed env; the GPG\n  passphrase moves to a chmod-600 passphrase file or an OpenClaw SecretRef\n  (`apiKey` injected as `CLOUD_BACKUP_GPG_PASSPHRASE`); the passphrase is\n  passed to gpg via file descriptor, never argv; plaintext\n  `skills.entries.cloud-backup.env.*` still resolves but warns loudly on\n  every run.\n- Durable plaintext leftovers are structurally impossible: archives are\n  built in a per-run mode-700 staging dir, removed on any exit, swept after\n  hard kills. (v1 stranded unencrypted tarballs whenever a step failed\n  between tar and gpg.)\n\n### Added\n\n- **OpenClaw secret-store integration**: `config.accessKeyRef` /\n  `secretKeyRef` / `sessionTokenRef` / `passphraseRef` accept OpenClaw\n  SecretRefs (`{source: env|file|exec, provider, id}` or `$NAME` templates),\n  resolved against the instance's `.secrets.providers` with the gateway's own\n  semantics (JSON-pointer file stores, env vars, protocolVersion-1 exec\n  backends such as 1Password wrappers). Configured-but-unresolvable refs\n  abort the run (exit 13/14) and show as `UNRESOLVABLE` in `status` — never a\n  silent fallback to a weaker tier.\n- `verify [name|--latest] [--deep]` — checksum + decrypt + listing; `--deep`\n  also runs the native `openclaw backup verify` on the decrypted archive.\n- Automatic post-backup verification (`config.verifyAfterBackup`, default\n  `quick`: streamed decrypt + entry-count match) and post-upload HEAD\n  verification (size + sha256 metadata).\n- Sensitivity verdict (`refs-only` vs `secret-material`) shown in `status`\n  and `--dry-run`, driving the forced-encryption policy;\n  `config.excludeSecrets` to produce secret-free archives instead.\n- `config.exclude` / `config.include` (state-relative globs), `--no-upload`,\n  `--json`, `backup --dry-run` plan output.\n- `prune --dry-run` with exact delete plan; failure-debris detection\n  (plaintext leftovers, incomplete sets) in `list`/`status`/`prune`.\n- Per-mode retention (count + days) — a daily `settings` run can no longer\n  evict your `full` archives.\n- `flock` concurrency lock, disk-space and cloud-reachability preflights,\n  documented exit-code map for cron agents.\n- `restore --target DIR` (staged), `--only GLOB` (selective), `--latest`;\n  v1 archives remain fully restorable.\n- New references: `credentials.md` (resolution chain + migration),\n  `setup-flow.md` (guided, gated first-time setup).\n\n### Deprecated (removed in v3)\n\n- `skills.entries.cloud-backup.env.ACCESS_KEY_ID` / `SECRET_ACCESS_KEY` /\n  `SESSION_TOKEN` / `GPG_PASSPHRASE` — still work, warn on every run.\n- Command `cleanup`, mode `skills`.\n\nv1.0.0 | 2026-02-17T14:59:22.743Z | auto\n\nInitial release of OpenClaw Cloud Backup.\n\n- Back up and restore OpenClaw configuration locally or to S3-compatible cloud storage (AWS S3, Cloudflare R2, Backblaze B2, MinIO, DigitalOcean Spaces).\n- Provides commands for backup, restore, list, and cleanup operations.\n- Supports automated daily backup scheduling with OpenClaw's native cron.\n- Includes reference docs for provider setup, security, and local config.\n- Credentials managed securely via OpenClaw config with support for agent-assisted or manual setup.\n- Focus on security: least-privilege, secret protection, dry-run restore, and archive path validation.\n\nArchive index:\n\nArchive v1.1.4: 15 files, 46522 bytes\n\nFiles: CHANGELOG.md (5334b), references/credentials.md (6277b), references/providers/aws-s3.md (2432b), references/providers/backblaze-b2.md (2187b), references/providers/cloudflare-r2.md (2301b), references/providers/digitalocean-spaces.md (2370b), references/providers/minio.md (2215b), references/providers/other.md (2938b), references/security.md (4792b), references/setup-flow.md (3330b), scripts/cloud-backup.sh (63208b), SECURITY.md (4512b), skill-card.md (3028b), SKILL.md (9217b), _meta.json (140b)\n\nFile v1.1.4:SKILL.md\n\n---\nname: cloud-backup\ndescription: Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw.\nmetadata: {\"openclaw\":{\"emoji\":\"☁️\",\"homepage\":\"https://github.com/obuchowski/openclaw-cloud-backup\",\"os\":[\"linux\",\"darwin\"],\"requires\":{\"bins\":[\"bash\",\"tar\",\"jq\"]},\"install\":[{\"kind\":\"brew\",\"formula\":\"awscli\",\"bins\":[\"aws\"]},{\"kind\":\"brew\",\"formula\":\"gnupg\",\"bins\":[\"gpg\"]}],\"primaryEnv\":\"CLOUD_BACKUP_GPG_PASSPHRASE\"}}\n---\n\n# OpenClaw Cloud Backup\n\nThe cloud layer for OpenClaw's native backup. Wraps `openclaw backup create`\n(config, credentials, consistent SQLite snapshots, workspace), then GPG-encrypts\nand uploads the archive to any S3-compatible bucket, with retention,\nverification, and staged restore.\n\nAll commands: `bash \"{baseDir}/scripts/cloud-backup.sh\" <subcommand>`\n\n## When to use this skill — and when not to\n\nAct ONLY on an explicit user request about OpenClaw backups: \"back up\nopenclaw\", \"restore my openclaw state\", \"set up cloud backups for openclaw\",\n\"/cloud-backup\", and similar.\n\n- Generic requests (\"back up my project\", \"save this file\", \"restore the\n  database\") are NOT for this skill. Ask what the user means before touching it.\n- NEVER run this skill as a side effect of another task, proactively, or\n  \"while you're at it\".\n- Read-only subcommands (`status`, `list`, `verify`, `schedule`, and any\n  `--dry-run`) may run freely once the user has asked about backups.\n  Everything else follows the gates below.\n\n## Confirmation gates\n\nBefore ANY state-changing action, show the user exactly what will happen and\nget an explicit yes. One gate per action — do not re-ask for things the user\njust confirmed, and never batch-confirm.\n\n| Action | What you MUST show before doing it |\n|---|---|\n| Write config (`openclaw config patch`) | Every key=value you will write, verbatim. Never write secrets — see Credentials. |\n| First backup to a new destination | Output of `backup <mode> --dry-run`: scope, sensitivity verdict, encryption status, target `s3://bucket/prefix`. |\n| Store/replace a credential | Only the file path + storage method (AWS profile / passphrase file). The secret value itself should not transit this conversation — the user runs those commands themselves. |\n| Restore | Step 1: always `restore <name> --dry-run` and show the file list. Step 2: state which paths will be overwritten (staged restores write to a fresh directory; `--in-place` overwrites live state), then require an explicit yes. Never skip the dry run. |\n| Prune | Output of `prune --dry-run`: which archives (local and remote) will be deleted, by name. |\n| Schedule creation | The exact `openclaw cron add ...` command and full payload text (see Scheduling). |\n\nRepeat manual backups to an already-confirmed destination need no new gate —\nthe user's request IS the confirmation. Still echo the one-line plan\n(\"full backup, encrypted, → s3://bucket/prefix\") before running.\n\n### Unattended runs (cron)\n\nA scheduled job's payload marks the run as operator-preconfirmed for `backup`\nand `prune` ONLY. In unattended runs: never restore, never change config,\nnever create or modify schedules, never store credentials.\n\n## Modes — what each backup contains\n\nEcho this table when the user asks what gets backed up:\n\n| Mode | Includes | Excludes by default | Sensitivity |\n|---|---|---|---|\n| `backup full` (default) | openclaw.json, credentials/, secret stores, state + agent SQLite snapshots, agent memory, workspace, installed skills | session transcripts, codex caches/logs, qmd embedding caches (models + index), tools/, media/, logs/, old backups | SENSITIVE — encryption forced |\n| `backup full --everything` | everything above PLUS session transcripts and codex history | previous backup archives only | SENSITIVE — encryption forced |\n| `backup settings` | openclaw.json, secret stores, credentials/, auth files | everything else | SENSITIVE — always encrypted, no opt-out |\n| `backup workspace` | workspace directories (skills, memory files) | all state/config | encrypted by default; `--no-encrypt` allowed here only |\n\n\"SENSITIVE — encryption forced\" means the script refuses to produce a plaintext\narchive for that scope (exit 14). Do not work around it; if the user explicitly\nwants a plaintext-shareable archive, offer `config.excludeSecrets=true` instead.\nUsers can tune scope with `config.exclude` / `config.include` (state-relative\nglobs).\n\n## Subcommands\n\n| Command | What it does | Gate? |\n|---|---|---|\n| `backup [full\\|settings\\|workspace] [--everything] [--no-upload] [--dry-run] [--json]` | Create archive, encrypt, upload, apply retention | dry-run free; see gates |\n| `list` | Local + remote backups; flags failure debris | no |\n| `status` | Health: last backup, credential sources, sensitivity verdict, schedule, reachability | no |\n| `verify [name\\|--latest] [--deep]` | Checksum + decrypt + listing; `--deep` adds `openclaw backup verify` | no |\n| `restore <name\\|--latest> [--target DIR \\| --in-place] [--only GLOB] [--dry-run] [--yes] [--force]` | Staged restore by default | YES — two-step |\n| `prune [--dry-run]` | Apply retention; remove failure debris | YES |\n| `schedule` | PRINT the opt-in cron command (creates nothing) | no |\n| `setup` | Setup checklist + connection test (never writes config) | config writes gated individually |\n\nExit codes (report them precisely, especially from cron): 0 ok ·\n3 ok-with-warnings · 4 usage · 10 another run holds the lock · 11 missing\ndependency · 12 insufficient disk · 13 cloud unreachable/bad credentials ·\n14 encryption required but unavailable · 20-25 create/filter/encrypt/upload/\nverify failures · 30 restore failure.\n\n## First-time setup\n\nFollow `references/setup-flow.md` step by step. Summary: choose provider →\nuser creates a least-privilege bucket-scoped key (per provider guide) → store\ncredentials OUTSIDE OpenClaw config (AWS profile recommended) → write\nnon-secret config (gate) → test connection → enable encryption with a\ngenerated passphrase file → first manual backup (gate) → only then offer\nscheduling (gate).\n\n## Credentials\n\nResolution order and storage rules: `references/credentials.md`. Hard rules:\n\n- NEVER write access keys or passphrases into openclaw.json (no\n  `skills.entries.cloud-backup.env.*`). Backups archive that file: a plaintext\n  credential in config is carried inside every archive it protects.\n- S3 credentials live in an AWS named profile (`config.profile`, recommended)\n  or operator-managed process env. The passphrase lives in a chmod-600\n  passphrase file (`config.passphraseFile`).\n- Operators who run OpenClaw's secret store can point the skill at it\n  instead: `config.accessKeyRef` / `config.secretKeyRef` /\n  `config.passphraseRef` accept OpenClaw SecretRefs ({source: env|file|exec})\n  resolved against `.secrets.providers` — including 1Password-style exec\n  providers. Configured-but-broken refs abort the run; they never fall back.\n- Prefer flows where the secret never appears in this conversation: the user\n  runs `aws configure --profile ...` and the passphrase generator themselves.\n- If the script prints a DEPRECATED warning about plaintext config\n  credentials, surface it to the user verbatim and offer the migration in\n  `references/credentials.md`.\n\n## Scheduling — strictly opt-in\n\nNEVER create, modify, or enable a cron job by default, implicitly, or because\n\"backups should be scheduled\". Skills document cron setup; only the user\nopts in.\n\nOffer scheduling exactly once, AFTER the first successful manual backup:\n\n> \"Backup verified. Want me to schedule this daily? I'd create this cron job —\n> nothing is scheduled until you confirm:\"\n\nThen run `schedule` to print the exact command, adjust time/timezone/delivery\nto the user's setup, show it in full, and wait for an explicit yes before\nrunning it. If the user declines: drop it, and do not re-offer on later runs\n(check `openclaw cron list` first — if a cloud-backup job exists, never offer).\nTo remove a schedule: `openclaw cron rm <id>`.\n\n## Error handling (for you, the agent)\n\n- Surface script stderr to the user verbatim (trim to the relevant lines).\n  Never summarize an error into vagueness, never hide a WARN.\n- Non-zero exit: diagnose using the exit-code table, explain, propose ONE fix.\n  Never auto-retry `restore`, `prune`, or any `openclaw cron` mutation.\n  `backup` may be retried once, only after the cause is fixed and the user\n  agrees.\n- Never invent bucket names, endpoints, or passphrases. Missing value → ask.\n- Exit 14 (encryption required, no passphrase) fails hard by design — do not\n  work around it with `--no-encrypt` or an ad-hoc passphrase. Run setup.\n- The script cleans its staging on failure; if it reports it could not, tell\n  the user the exact leftover path.\n\n## Reference docs (read only when needed)\n\n- `references/credentials.md` — resolution chain, v1→v2 migration, warnings\n- `references/setup-flow.md` — guided first-time setup\n- `references/providers/{aws-s3,cloudflare-r2,backblaze-b2,digitalocean-spaces,minio,other}.md`\n- `references/security.md` — threat model, restore safety, incident response\n\nFile v1.1.4:_meta.json\n\n{\n  \"ownerId\": \"kn73d89b2a038m7cxgqww1w5ah81a7ar\",\n  \"slug\": \"openclaw-cloud-backup\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1782999902362\n}\n\nFile v1.1.4:references/credentials.md\n\n# Credentials — where each secret lives, and why\n\nThis skill needs up to three secrets: an S3 access key id, an S3 secret key,\nand a GPG passphrase. **None of them belong in openclaw.json.** Backups\narchive that file — a credential stored there rides along inside every\narchive it protects.\n\nThe recommended setup is two chmod-600 files:\n\n| Secret | Home | How |\n|---|---|---|\n| S3 key pair | AWS named profile (`~/.aws/credentials`) | `aws configure --profile openclaw-backup && chmod 600 ~/.aws/credentials`, then set `config.profile` |\n| GPG passphrase | passphrase file | `umask 077 && openssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase`, then set `config.passphraseFile` |\n\n## Resolution order (what the script actually does)\n\n**S3 credentials** (highest first):\n\n1. Process env `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` /\n   `AWS_SESSION_TOKEN` — operator-injected (systemd `EnvironmentFile=`,\n   gateway environment). Never overridden.\n2. **OpenClaw secret refs** — `config.accessKeyRef` + `config.secretKeyRef`\n   (+ optional `config.sessionTokenRef`). See \"Using the OpenClaw secret\n   store\" below. A configured-but-unresolvable ref aborts the run (exit 13) —\n   never a silent fallthrough to a weaker tier.\n3. `config.profile` → named profile in `~/.aws/credentials` (mode 600).\n   **Recommended simple default.** The skill passes `--profile`; the aws CLI\n   reads its own standard store. The secret never touches OpenClaw config or\n   this chat — and `~/.aws` is OUTSIDE the backup scope, so the key never\n   travels inside the archives it protects.\n4. DEPRECATED: `skills.entries.cloud-backup.env.ACCESS_KEY_ID` /\n   `.SECRET_ACCESS_KEY` / `.SESSION_TOKEN` plaintext in openclaw.json.\n   Still works in v2; warns loudly on every run; removed in v3.\n\n**GPG passphrase** (highest first):\n\n1. Process env `GPG_PASSPHRASE` — operator-injected.\n2. Process env `CLOUD_BACKUP_GPG_PASSPHRASE` — injected by OpenClaw from\n   `skills.entries.cloud-backup.apiKey` (which may itself be a SecretRef).\n   Resolves only inside the OpenClaw agent runtime, so a bare-shell\n   `cloud-backup.sh backup` won't see it — prefer `passphraseRef` below.\n3. **OpenClaw secret ref** — `config.passphraseRef` (below). Works from any\n   shell; aborts with exit 14 if configured but unresolvable.\n4. `config.passphraseFile` — path to a mode-600 file. **Recommended simple\n   default.** The script refuses world-readable files and warns on group\n   access. The passphrase is passed to gpg over a file descriptor — never on\n   a command line (v1 leaked it into `ps`/`/proc/*/cmdline`).\n5. DEPRECATED: `skills.entries.cloud-backup.env.GPG_PASSPHRASE` plaintext in\n   openclaw.json. Warns on every run; removed in v3.\n\n`status` always prints where each secret resolved from — check it whenever\nyou are unsure which tier is active.\n\n## Using the OpenClaw secret store (config.*Ref)\n\nIf you already run OpenClaw's secret system (`openclaw secrets configure`,\n`.secrets.providers` in openclaw.json), the skill hooks straight into it —\none credential model for the whole instance, 1Password-style backends\nincluded. The `*Ref` keys accept the same shapes OpenClaw uses everywhere:\na SecretRef object `{source, provider, id}` or a `$NAME` / `${NAME}` env\ntemplate.\n\n```json\n{ \"skills\": { \"entries\": { \"cloud-backup\": { \"config\": {\n  \"accessKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/accessKeyId\" },\n  \"secretKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/secretAccessKey\" },\n  \"passphraseRef\": { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/gpgPassphrase\" }\n} } } } }\n```\n\nSupported sources (resolved with the gateway's own semantics):\n\n- **file** — provider `{source: \"file\", path, mode: \"json\"|\"singleValue\"}`;\n  in json mode (the default) `id` is a JSON pointer into the chmod-600 store\n  file, e.g. `/cloudBackup/gpgPassphrase`.\n- **env** — `id` is the environment variable name.\n- **exec** — provider `{source: \"exec\", command, args?, jsonOnly?}`; the\n  skill speaks the protocolVersion-1 contract (request object on stdin,\n  `values` map on stdout), so the same resolver you use for the gateway —\n  e.g. a 1Password `op read` wrapper — works unchanged. Plugin-integration\n  exec providers resolve only inside the gateway and are rejected with a\n  clear error.\n\nNotes:\n\n- Configured refs that fail to resolve abort instead of falling back;\n  `status` shows `UNRESOLVABLE` with the reason.\n- Archive-scope trade-off: a file-provider store under `~/.openclaw` travels\n  inside every (encrypted) archive; an AWS profile never travels at all.\n  Both are fine — just know which you picked.\n- A literal secret string in a `*Ref` key technically works but is plaintext\n  in config — the sensitivity verdict will flag it. Use a real ref.\n\n## Generating a passphrase\n\n```bash\numask 077\nmkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n**Immediately store a copy in your password manager.** Without the\npassphrase, encrypted backups are unrecoverable — that is the point of them.\nIf you ever rotate it, keep the old one labeled with its date range: older\narchives still need it until they age out of retention.\n\n## Migrating from v1 (plaintext in openclaw.json)\n\n```bash\n# 1. S3 keys → profile (run yourself; do not paste keys into agent chat)\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n\n# 2. Passphrase → file (see above). If you keep the SAME passphrase, just move\n#    it; if you pick a new one, save BOTH in your password manager.\n\n# 3. Remove the plaintext block, then verify\nopenclaw config patch 'skills.entries.cloud-backup.env=null'\njq '.skills.entries[\"cloud-backup\"]' ~/.openclaw/openclaw.json   # no \"env\" block\nbash scripts/cloud-backup.sh status                              # no DEPRECATED warnings\n```\n\nConsider the old key exposed (it lived in config, which earlier backups\narchived): rotate it at the provider after the new chain is verified.\n\nFile v1.1.4:references/providers/aws-s3.md\n\n# AWS S3\n\n## 1. Create a private bucket\n\n1. AWS Console → S3 → **Create bucket**\n2. Keep **Block Public Access** enabled (all four checkboxes)\n3. Enable **Bucket Versioning** (recommended — protects against overwrites)\n4. Use **SSE-S3** encryption (default, free)\n\n## 2. Create a least-privilege key\n\nCreate a dedicated IAM user with programmatic access. Never use root keys.\nAttach this policy (replace `YOUR_BUCKET`):\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Sid\": \"ListBucket\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Sid\": \"ObjectAccess\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\nIAM → Users → Create user → attach policy → Create access key.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\nOn EC2/ECS, prefer an instance/task role and skip stored keys entirely.\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"YOUR_BUCKET\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n**No `endpoint`** — AWS S3 is the one provider where it stays unset.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Common regions: `us-east-1`, `us-west-2`, `eu-west-1`, `eu-central-1`.\n- If using S3 Object Lock or Glacier, extend the IAM policy accordingly.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/providers/backblaze-b2.md\n\n# Backblaze B2 (S3-compatible API)\n\n## 1. Create a private bucket\n\n1. Backblaze Console → **B2 Cloud Storage** → **Create a Bucket**\n2. Set to **Private**\n3. Disable Object Lock unless you need immutable backups\n\n## 2. Create a least-privilege key\n\n1. **App Keys** → **Add a New Application Key**\n2. **Restrict to your backup bucket**\n3. Allow: `listBuckets`, `listFiles`, `readFiles`, `writeFiles`, `deleteFiles`\n4. Note the **keyID** (= access key) and **applicationKey** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste keyID + applicationKey\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-west-004\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.us-west-004.backblazeb2.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nThe region is on the bucket details page and must match the endpoint.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Free tier: 10 GB storage, 1 GB/day egress.\n- Bucket-scoped application keys cannot list other buckets — that is the point.\n- Rotating the master key revokes ALL application keys.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/providers/cloudflare-r2.md\n\n# Cloudflare R2\n\n## 1. Create a private bucket\n\n1. Cloudflare Dashboard → **R2 Object Storage** → **Create bucket**\n2. Pick a name (lowercase, no dots); location hint \"Automatic\" is fine\n3. Recommended: add a lifecycle rule **Abort incomplete multipart uploads\n   after 1 day** (cleans up interrupted uploads)\n\n## 2. Create a least-privilege key\n\n1. R2 → **Manage R2 API Tokens** → **Create API token**\n2. Permissions: **Object Read & Write**\n3. Scope: **restrict to your backup bucket only**\n4. Note the **Access Key ID** and **Secret Access Key** for the next step\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste key id + secret; region: auto\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"auto\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://<ACCOUNT_ID>.r2.cloudflarestorage.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nFind your Account ID in the dashboard sidebar or the R2 overview page.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- R2 region is always `auto` — single global namespace.\n- Zero egress fees; free tier: 10 GB storage, 10M reads / 1M writes per month.\n- Signature errors usually mean a wrong Account ID in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/providers/digitalocean-spaces.md\n\n# DigitalOcean Spaces\n\n## 1. Create a private Space\n\n1. DigitalOcean Console → **Spaces Object Storage** → **Create a Space**\n2. Choose a datacenter region (`nyc3`, `sfo3`, `ams3`, `sgp1`, `fra1`, …)\n3. Restrict file listing: **Private**\n\n## 2. Create a key — read the warning first\n\n> **WARNING: Spaces keys are account-wide.** DigitalOcean does not support\n> per-Space scoping — this key can read, write, and delete EVERY Space in\n> your account. Prefer a dedicated team/project for backups, and rotate more\n> aggressively than usual (30–60 days). If account-wide blast radius is\n> unacceptable, pick a provider with bucket-scoped keys (R2, B2, AWS).\n\n1. **API** → **Spaces Keys** → **Generate New Key**\n2. Note the **Key** (= access key) and **Secret** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-space\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"nyc3\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://nyc3.digitaloceanspaces.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region in the endpoint and `config.region` must match.\n- No free tier (Spaces starts at $5/mo for 250 GB).\n- `SignatureDoesNotMatch` → endpoint region doesn't match the Space's region.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/providers/minio.md\n\n# MinIO\n\n## 1. Create a private bucket\n\n1. MinIO Console → **Buckets** → **Create Bucket**\n2. Set access to **Private**\n\n## 2. Create a least-privilege key\n\nNever use the admin credentials (`minioadmin`) for backups.\n\n1. MinIO Console → **Access Keys** → **Create Access Key**\n2. Attach a policy restricted to the one bucket:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Effect\": \"Allow\", \"Action\": [\"s3:ListBucket\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://minio.example.com:9000\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region can be anything; `us-east-1` is conventional.\n- Self-signed TLS: set `AWS_CA_BUNDLE=/path/to/ca.pem` in the environment.\n- Non-standard port goes in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/providers/other.md\n\n# Other / Custom S3-Compatible Provider\n\nAny storage service with an S3-compatible API works. You need three things:\na **private bucket**, the **S3 endpoint URL**, and a **key pair** — scoped to\nthe one bucket if your provider supports scoping (use it if so).\n\n## 1. Finding the endpoint\n\nAlways a full URL. Common patterns:\n\n- `https://s3.<region>.<provider>.com`\n- `https://<account-id>.r2.cloudflarestorage.com`\n- `https://<region>.digitaloceanspaces.com`\n- `https://minio.your-server.com:9000`\n\nLook for \"S3 API endpoint\" in your provider's compatibility docs.\n\n## 2. Create a least-privilege key\n\nLook for \"API keys\", \"access keys\", \"S3 credentials\", or \"application keys\"\nin the console. If the provider supports key scoping, restrict to the backup\nbucket with list/read/write/delete only.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.your-provider.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nIf unsure about region, use `us-east-1` or `auto`.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Verifying compatibility\n\nThe skill uses only basic operations: `aws s3 cp/ls/rm` plus a\n`head-object` check. No presigned URLs, no bucket creation, no ACLs. If\n`aws s3 ls s3://your-bucket/ --endpoint-url https://... --profile openclaw-backup`\nworks, you're good. (Custom object metadata is used for upload verification;\nproviders that drop it just skip the sha check, size is still verified.)\n\n## Troubleshooting\n\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch; try `region=auto`.\n- **SSL errors** — self-signed certs: `AWS_CA_BUNDLE=/path/to/ca.pem`.\n- **Connection refused** — include the port in the endpoint.\n- **Bucket-name DNS errors** — set `AWS_S3_FORCE_PATH_STYLE=true` in the env.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.4:references/security.md\n\n# Security — threat model, restore safety, incident response\n\n## Threat model (what the encryption is for)\n\nArchive encryption protects your backups **at rest in the bucket and in\nleak scenarios**: bucket compromise, provider insiders, a mislaid archive\nfile. It does **not** protect against host compromise — anyone with a shell\non your host already reads `~/.openclaw` directly.\n\nThat asymmetry drives the credential policy:\n\n- Host-side secrets in mode-600 files (`~/.aws/credentials`, the passphrase\n  file) are acceptable: they add no exposure beyond the host itself.\n- Secrets inside `openclaw.json` are NOT acceptable: **this skill archives\n  that file.** A credential stored there is replicated into every backup it\n  protects — exposure multiplied by retention count × destinations. This is\n  why v2 deprecates `skills.entries.cloud-backup.env.*` and why the docs\n  never suggest it.\n\n## Enforcement in the script (v2)\n\n- **Sensitivity verdict**: before every backup, the script detects whether\n  the archive scope contains real secret material (file-provider secret\n  stores, `credentials/`, legacy auth files, plaintext values in\n  openclaw.json). If it does, encryption is **forced** — plaintext output for\n  that scope fails with exit 14. `config.excludeSecrets=true` excludes the\n  detected stores instead (restores from such archives need secrets\n  re-provisioned — pair with an encrypted `backup settings`).\n- **No durable plaintext**: archives are produced inside a per-run mode-700\n  staging directory that is removed on any exit (and swept on the next run\n  after a hard kill). v1 could leave unencrypted tarballs behind when a step\n  failed; v2 cannot.\n- **No passphrase on command lines**: gpg receives the passphrase over a file\n  descriptor. v1 passed `--passphrase <value>` on argv, visible in\n  `ps`/`/proc/*/cmdline` for the duration of every run.\n- **Verified uploads**: sha256 sidecars, decrypt-and-list verification after\n  encryption, and a HEAD check (size + sha metadata) after upload.\n\n## Credential rules\n\n- Least-privilege, **bucket-scoped** keys (see the provider guides). Never\n  account-wide or root keys.\n- Storage: AWS named profile or operator-managed process env for S3; a\n  mode-600 passphrase file or OpenClaw SecretRef for GPG. Details and\n  resolution order: `references/credentials.md`.\n- Never commit credentials to git. `~/.openclaw/openclaw.json` should be\n  mode 600 regardless.\n- Rotate keys every ~90 days and immediately on any suspicion.\n\n## Restore safety\n\n1. Always `restore --dry-run` first — lists contents without extracting.\n2. Checksums are verified before anything else; decryption before listing.\n3. Tar member paths are validated — absolute paths and `..` traversal are\n   rejected before extraction.\n4. Restores are **staged by default** (a fresh mode-700 directory, with\n   printed next steps). `--in-place` overwrites live state and requires an\n   interactive typed confirmation, or `--yes --force` for automation.\n5. Cross-host caution: native archives record their original state dir; an\n   in-place restore onto a host with a different state dir is refused — go\n   through `--target`.\n\n## Troubleshooting\n\n- **`Unable to locate credentials`** — no profile/env configured. Set\n  `config.profile` (recommended) — see `references/credentials.md`.\n- **`AccessDenied`** — the key lacks `ListBucket` / `GetObject` / `PutObject`\n  / `DeleteObject` on the target bucket.\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch (check provider\n  guide), or system clock skew.\n- **`Could not connect to the endpoint URL`** — AWS: leave `endpoint` unset;\n  every other provider: `endpoint` is required.\n- **Checksum mismatch on restore** — re-download; if persistent, treat the\n  remote object as corrupted and restore an older set.\n- **exit 14** — the scope contains secret material and no passphrase is\n  configured. Set `config.passphraseFile`; do not bypass.\n\n## Incident response (key suspected leaked)\n\nRotate-first, revoke-last — never leave a window with zero working backups:\n\n1. Create a NEW bucket-scoped key in the provider console (old key stays\n   valid for now).\n2. Update the AWS profile (`aws configure --profile openclaw-backup`).\n3. Run `backup full` + `verify --latest` to prove the new chain works.\n4. Audit recent bucket activity for unexpected reads/writes/deletes.\n5. Revoke the OLD key.\n6. If the key ever lived in openclaw.json: archives made during that period\n   contain it. Prune them (`prune`, plus manual `aws s3 rm` for anything\n   outside retention) or treat the data they protect as exposed.\n7. If the passphrase leaked: pick a new one, keep the old labeled by date\n   range (old archives still need it), and re-create current backups under\n   the new passphrase.\n\nFile v1.1.4:references/setup-flow.md\n\n# First-time setup — guided flow (for the agent)\n\nPrinciples: the user understands every consequence before it happens; secrets\nnever transit the conversation; every config write is shown verbatim and\nconfirmed first; nothing is scheduled without an explicit opt-in.\n\n## Step 0 — Preflight\n\nRun `setup`. It prints the checklist, current config, dependency status, and\n(if cloud is configured) a connection test. It never writes anything.\n\n## Step 1 — Provider\n\nAsk which provider: AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean Spaces,\nMinIO, or another S3-compatible service. Read the matching\n`references/providers/<provider>.md`.\n\n## Step 2 — Bucket + least-privilege key\n\nEcho the provider guide's bucket and key steps. The user performs them in the\nprovider console: private bucket, then a key scoped to that one bucket (where\nthe provider supports scoping).\n\n## Step 3 — Credential home\n\nOffer exactly two options (never a third):\n\n1. **AWS profile (recommended)** — the user runs, themselves:\n   ```bash\n   aws configure --profile openclaw-backup\n   chmod 600 ~/.aws/credentials\n   ```\n   The key never appears in this chat.\n2. **Process env** — for operators who manage gateway env (systemd\n   `EnvironmentFile=`): `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`.\n\nPlaintext in openclaw.json is not offered. If the user asks for it, explain\nthe amplifier (backups archive the config) and point to\n`references/credentials.md`.\n\n## Step 4 — Config write (GATE)\n\nShow every patch verbatim, wait for an explicit yes, then run:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"<bucket>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"<endpoint>\"'   # non-AWS only\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"<region>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Step 5 — Connection test\n\nRun `setup` again (or `status`) and confirm \"Connected / Reachable ✓\".\n\n## Step 6 — Encryption (GATE on the config writes)\n\nTell the user:\n\n> \"Backups of this scope contain your OpenClaw config, credential store, and\n> agent databases, so encryption is required. Generate a strong passphrase\n> into a 600-mode file (run it yourself), then store a copy in your password\n> manager — without it, backups are unrecoverable:\"\n\n```bash\numask 077 && mkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\n```\n\nThen (after confirmation) set:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n(`config.encrypt` already defaults to true in v2.) Advanced alternative —\nSecretRef via `apiKey`: see `references/credentials.md`.\n\n## Step 7 — First backup (GATE)\n\n1. Run `backup full --dry-run`; show the user the scope, excludes, sensitivity\n   verdict, and destination.\n2. On yes: run `backup full`, then `verify --latest`.\n3. Report: archive name, size, encrypted ✓, uploaded ✓, verified ✓.\n\n## Step 8 — Scheduling offer (only now, only once)\n\nPer SKILL.md \"Scheduling — strictly opt-in\": offer once, show the exact\n`openclaw cron add` command from `schedule`, and create nothing without an\nexplicit yes. \"No\" is a complete answer — do not re-offer.\n\nFile v1.1.4:CHANGELOG.md\n\n# Changelog\n\n## 2.0.2 — 2026-07-02\n\n### Changed\n\n- Lean filter now excludes `agents/*/qmd/xdg-cache/**` (qmd embedding caches:\n  downloaded GGUF models and `index.sqlite`). Both are rebuildable from the\n  indexed sources and had inflated full archives from ~170 MB to ~1.17 GB once\n  qmd started caching local embedding models under agent state.\n\n## 2.0.1 — 2026-06-11\n\n### Changed\n\n- Skill description polished (secrets-safe encryption, lean modes, opt-in\n  cron, staged restore) — the explicit-intent activation gate is unchanged.\n  No functional changes; identical script behavior to 2.0.0.\n\n## 2.0.0 — 2026-06-11\n\nComplete redesign: the skill is now **the cloud layer for OpenClaw's native\nbackup**, and was rebuilt around the ClawHub security-scan findings of v1.1.5\n(see SECURITY.md for the point-by-point map).\n\n### Breaking\n\n- The `full` engine is `openclaw backup create` (consistent SQLite snapshots,\n  embedded manifest, multi-directory coverage) plus a lean filter. The\n  `openclaw` CLI is therefore required for `backup full`.\n- `full` is **lean by default**: session transcripts, Codex caches/log\n  databases, `tools/`, `media/`, `logs/`, and old backups are excluded.\n  `--everything` (or `config.everything=true`) restores the old behavior.\n- `config.encrypt` now defaults to **true**, and encryption is **forced**\n  (exit 14 rather than a warning) whenever the archive scope contains real\n  secret material — detected automatically.\n- The local archive store moves from `~/.openclaw/backups` to\n  `~/.local/share/openclaw-backups` (`config.localDir`). Required: the native\n  engine refuses output inside a source path, and v1's location made every\n  new full backup swallow all previous ones.\n- `cleanup` renamed to `prune`; mode `skills` folded into `workspace` (both\n  aliases still work, with warnings).\n- Restores are **staged by default** (new directory + printed next steps);\n  in-place restores moved behind `--in-place` with typed confirmation\n  (`--yes --force` for automation).\n\n### Security (ClawHub scan remediation)\n\n- **SQP-1**: activation narrowed to explicit OpenClaw-backup intent;\n  SKILL.md adds per-action confirmation gates (config writes, first upload,\n  credential storage, restore, prune, schedule creation) and an\n  unattended-run policy.\n- **SQP-2 (cron)**: the daily job is no longer created by default.\n  Scheduling is opt-in, offered once after the first successful manual\n  backup, with the exact `openclaw cron add` command and payload shown\n  beforehand. The new `schedule` subcommand only prints.\n- **SQP-2 (credentials)**: all provider docs now lead with least-privilege\n  bucket-scoped keys in AWS named profiles or operator-managed env; the GPG\n  passphrase moves to a chmod-600 passphrase file or an OpenClaw SecretRef\n  (`apiKey` injected as `CLOUD_BACKUP_GPG_PASSPHRASE`); the passphrase is\n  passed to gpg via file descriptor, never argv; plaintext\n  `skills.entries.cloud-backup.env.*` still resolves but warns loudly on\n  every run.\n- Durable plaintext leftovers are structurally impossible: archives are\n  built in a per-run mode-700 staging dir, removed on any exit, swept after\n  hard kills. (v1 stranded unencrypted tarballs whenever a step failed\n  between tar and gpg.)\n\n### Added\n\n- **OpenClaw secret-store integration**: `config.accessKeyRef` /\n  `secretKeyRef` / `sessionTokenRef` / `passphraseRef` accept OpenClaw\n  SecretRefs (`{source: env|file|exec, provider, id}` or `$NAME` templates),\n  resolved against the instance's `.secrets.providers` with the gateway's own\n  semantics (JSON-pointer file stores, env vars, protocolVersion-1 exec\n  backends such as 1Password wrappers). Configured-but-unresolvable refs\n  abort the run (exit 13/14) and show as `UNRESOLVABLE` in `status` — never a\n  silent fallback to a weaker tier.\n- `verify [name|--latest] [--deep]` — checksum + decrypt + listing; `--deep`\n  also runs the native `openclaw backup verify` on the decrypted archive.\n- Automatic post-backup verification (`config.verifyAfterBackup`, default\n  `quick`: streamed decrypt + entry-count match) and post-upload HEAD\n  verification (size + sha256 metadata).\n- Sensitivity verdict (`refs-only` vs `secret-material`) shown in `status`\n  and `--dry-run`, driving the forced-encryption policy;\n  `config.excludeSecrets` to produce secret-free archives instead.\n- `config.exclude` / `config.include` (state-relative globs), `--no-upload`,\n  `--json`, `backup --dry-run` plan output.\n- `prune --dry-run` with exact delete plan; failure-debris detection\n  (plaintext leftovers, incomplete sets) in `list`/`status`/`prune`.\n- Per-mode retention (count + days) — a daily `settings` run can no longer\n  evict your `full` archives.\n- `flock` concurrency lock, disk-space and cloud-reachability preflights,\n  documented exit-code map for cron agents.\n- `restore --target DIR` (staged), `--only GLOB` (selective), `--latest`;\n  v1 archives remain fully restorable.\n- New references: `credentials.md` (resolution chain + migration),\n  `setup-flow.md` (guided, gated first-time setup).\n\n### Deprecated (removed in v3)\n\n- `skills.entries.cloud-backup.env.ACCESS_KEY_ID` / `SECRET_ACCESS_KEY` /\n  `SESSION_TOKEN` / `GPG_PASSPHRASE` — still work, warn on every run.\n- Command `cleanup`, mode `skills`.\n\n## 1.1.5 — 2026-02\n\nLast v1 release. See git history.\n\nArchive v1.1.3: 15 files, 46337 bytes\n\nFiles: CHANGELOG.md (4998b), references/credentials.md (6277b), references/providers/aws-s3.md (2432b), references/providers/backblaze-b2.md (2187b), references/providers/cloudflare-r2.md (2301b), references/providers/digitalocean-spaces.md (2370b), references/providers/minio.md (2215b), references/providers/other.md (2938b), references/security.md (4792b), references/setup-flow.md (3330b), scripts/cloud-backup.sh (63156b), SECURITY.md (4512b), skill-card.md (3234b), SKILL.md (9178b), _meta.json (140b)\n\nFile v1.1.3:SKILL.md\n\n---\nname: cloud-backup\ndescription: Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw.\nmetadata: {\"openclaw\":{\"emoji\":\"☁️\",\"homepage\":\"https://github.com/obuchowski/openclaw-cloud-backup\",\"os\":[\"linux\",\"darwin\"],\"requires\":{\"bins\":[\"bash\",\"tar\",\"jq\"]},\"install\":[{\"kind\":\"brew\",\"formula\":\"awscli\",\"bins\":[\"aws\"]},{\"kind\":\"brew\",\"formula\":\"gnupg\",\"bins\":[\"gpg\"]}],\"primaryEnv\":\"CLOUD_BACKUP_GPG_PASSPHRASE\"}}\n---\n\n# OpenClaw Cloud Backup\n\nThe cloud layer for OpenClaw's native backup. Wraps `openclaw backup create`\n(config, credentials, consistent SQLite snapshots, workspace), then GPG-encrypts\nand uploads the archive to any S3-compatible bucket, with retention,\nverification, and staged restore.\n\nAll commands: `bash \"{baseDir}/scripts/cloud-backup.sh\" <subcommand>`\n\n## When to use this skill — and when not to\n\nAct ONLY on an explicit user request about OpenClaw backups: \"back up\nopenclaw\", \"restore my openclaw state\", \"set up cloud backups for openclaw\",\n\"/cloud-backup\", and similar.\n\n- Generic requests (\"back up my project\", \"save this file\", \"restore the\n  database\") are NOT for this skill. Ask what the user means before touching it.\n- NEVER run this skill as a side effect of another task, proactively, or\n  \"while you're at it\".\n- Read-only subcommands (`status`, `list`, `verify`, `schedule`, and any\n  `--dry-run`) may run freely once the user has asked about backups.\n  Everything else follows the gates below.\n\n## Confirmation gates\n\nBefore ANY state-changing action, show the user exactly what will happen and\nget an explicit yes. One gate per action — do not re-ask for things the user\njust confirmed, and never batch-confirm.\n\n| Action | What you MUST show before doing it |\n|---|---|\n| Write config (`openclaw config patch`) | Every key=value you will write, verbatim. Never write secrets — see Credentials. |\n| First backup to a new destination | Output of `backup <mode> --dry-run`: scope, sensitivity verdict, encryption status, target `s3://bucket/prefix`. |\n| Store/replace a credential | Only the file path + storage method (AWS profile / passphrase file). The secret value itself should not transit this conversation — the user runs those commands themselves. |\n| Restore | Step 1: always `restore <name> --dry-run` and show the file list. Step 2: state which paths will be overwritten (staged restores write to a fresh directory; `--in-place` overwrites live state), then require an explicit yes. Never skip the dry run. |\n| Prune | Output of `prune --dry-run`: which archives (local and remote) will be deleted, by name. |\n| Schedule creation | The exact `openclaw cron add ...` command and full payload text (see Scheduling). |\n\nRepeat manual backups to an already-confirmed destination need no new gate —\nthe user's request IS the confirmation. Still echo the one-line plan\n(\"full backup, encrypted, → s3://bucket/prefix\") before running.\n\n### Unattended runs (cron)\n\nA scheduled job's payload marks the run as operator-preconfirmed for `backup`\nand `prune` ONLY. In unattended runs: never restore, never change config,\nnever create or modify schedules, never store credentials.\n\n## Modes — what each backup contains\n\nEcho this table when the user asks what gets backed up:\n\n| Mode | Includes | Excludes by default | Sensitivity |\n|---|---|---|---|\n| `backup full` (default) | openclaw.json, credentials/, secret stores, state + agent SQLite snapshots, agent memory, workspace, installed skills | session transcripts, codex caches/logs, tools/, media/, logs/, old backups | SENSITIVE — encryption forced |\n| `backup full --everything` | everything above PLUS session transcripts and codex history | previous backup archives only | SENSITIVE — encryption forced |\n| `backup settings` | openclaw.json, secret stores, credentials/, auth files | everything else | SENSITIVE — always encrypted, no opt-out |\n| `backup workspace` | workspace directories (skills, memory files) | all state/config | encrypted by default; `--no-encrypt` allowed here only |\n\n\"SENSITIVE — encryption forced\" means the script refuses to produce a plaintext\narchive for that scope (exit 14). Do not work around it; if the user explicitly\nwants a plaintext-shareable archive, offer `config.excludeSecrets=true` instead.\nUsers can tune scope with `config.exclude` / `config.include` (state-relative\nglobs).\n\n## Subcommands\n\n| Command | What it does | Gate? |\n|---|---|---|\n| `backup [full\\|settings\\|workspace] [--everything] [--no-upload] [--dry-run] [--json]` | Create archive, encrypt, upload, apply retention | dry-run free; see gates |\n| `list` | Local + remote backups; flags failure debris | no |\n| `status` | Health: last backup, credential sources, sensitivity verdict, schedule, reachability | no |\n| `verify [name\\|--latest] [--deep]` | Checksum + decrypt + listing; `--deep` adds `openclaw backup verify` | no |\n| `restore <name\\|--latest> [--target DIR \\| --in-place] [--only GLOB] [--dry-run] [--yes] [--force]` | Staged restore by default | YES — two-step |\n| `prune [--dry-run]` | Apply retention; remove failure debris | YES |\n| `schedule` | PRINT the opt-in cron command (creates nothing) | no |\n| `setup` | Setup checklist + connection test (never writes config) | config writes gated individually |\n\nExit codes (report them precisely, especially from cron): 0 ok ·\n3 ok-with-warnings · 4 usage · 10 another run holds the lock · 11 missing\ndependency · 12 insufficient disk · 13 cloud unreachable/bad credentials ·\n14 encryption required but unavailable · 20-25 create/filter/encrypt/upload/\nverify failures · 30 restore failure.\n\n## First-time setup\n\nFollow `references/setup-flow.md` step by step. Summary: choose provider →\nuser creates a least-privilege bucket-scoped key (per provider guide) → store\ncredentials OUTSIDE OpenClaw config (AWS profile recommended) → write\nnon-secret config (gate) → test connection → enable encryption with a\ngenerated passphrase file → first manual backup (gate) → only then offer\nscheduling (gate).\n\n## Credentials\n\nResolution order and storage rules: `references/credentials.md`. Hard rules:\n\n- NEVER write access keys or passphrases into openclaw.json (no\n  `skills.entries.cloud-backup.env.*`). Backups archive that file: a plaintext\n  credential in config is carried inside every archive it protects.\n- S3 credentials live in an AWS named profile (`config.profile`, recommended)\n  or operator-managed process env. The passphrase lives in a chmod-600\n  passphrase file (`config.passphraseFile`).\n- Operators who run OpenClaw's secret store can point the skill at it\n  instead: `config.accessKeyRef` / `config.secretKeyRef` /\n  `config.passphraseRef` accept OpenClaw SecretRefs ({source: env|file|exec})\n  resolved against `.secrets.providers` — including 1Password-style exec\n  providers. Configured-but-broken refs abort the run; they never fall back.\n- Prefer flows where the secret never appears in this conversation: the user\n  runs `aws configure --profile ...` and the passphrase generator themselves.\n- If the script prints a DEPRECATED warning about plaintext config\n  credentials, surface it to the user verbatim and offer the migration in\n  `references/credentials.md`.\n\n## Scheduling — strictly opt-in\n\nNEVER create, modify, or enable a cron job by default, implicitly, or because\n\"backups should be scheduled\". Skills document cron setup; only the user\nopts in.\n\nOffer scheduling exactly once, AFTER the first successful manual backup:\n\n> \"Backup verified. Want me to schedule this daily? I'd create this cron job —\n> nothing is scheduled until you confirm:\"\n\nThen run `schedule` to print the exact command, adjust time/timezone/delivery\nto the user's setup, show it in full, and wait for an explicit yes before\nrunning it. If the user declines: drop it, and do not re-offer on later runs\n(check `openclaw cron list` first — if a cloud-backup job exists, never offer).\nTo remove a schedule: `openclaw cron rm <id>`.\n\n## Error handling (for you, the agent)\n\n- Surface script stderr to the user verbatim (trim to the relevant lines).\n  Never summarize an error into vagueness, never hide a WARN.\n- Non-zero exit: diagnose using the exit-code table, explain, propose ONE fix.\n  Never auto-retry `restore`, `prune`, or any `openclaw cron` mutation.\n  `backup` may be retried once, only after the cause is fixed and the user\n  agrees.\n- Never invent bucket names, endpoints, or passphrases. Missing value → ask.\n- Exit 14 (encryption required, no passphrase) fails hard by design — do not\n  work around it with `--no-encrypt` or an ad-hoc passphrase. Run setup.\n- The script cleans its staging on failure; if it reports it could not, tell\n  the user the exact leftover path.\n\n## Reference docs (read only when needed)\n\n- `references/credentials.md` — resolution chain, v1→v2 migration, warnings\n- `references/setup-flow.md` — guided first-time setup\n- `references/providers/{aws-s3,cloudflare-r2,backblaze-b2,digitalocean-spaces,minio,other}.md`\n- `references/security.md` — threat model, restore safety, incident response\n\nFile v1.1.3:_meta.json\n\n{\n  \"ownerId\": \"kn73d89b2a038m7cxgqww1w5ah81a7ar\",\n  \"slug\": \"openclaw-cloud-backup\",\n  \"version\": \"1.1.3\",\n  \"publishedAt\": 1782319472703\n}\n\nFile v1.1.3:references/credentials.md\n\n# Credentials — where each secret lives, and why\n\nThis skill needs up to three secrets: an S3 access key id, an S3 secret key,\nand a GPG passphrase. **None of them belong in openclaw.json.** Backups\narchive that file — a credential stored there rides along inside every\narchive it protects.\n\nThe recommended setup is two chmod-600 files:\n\n| Secret | Home | How |\n|---|---|---|\n| S3 key pair | AWS named profile (`~/.aws/credentials`) | `aws configure --profile openclaw-backup && chmod 600 ~/.aws/credentials`, then set `config.profile` |\n| GPG passphrase | passphrase file | `umask 077 && openssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase`, then set `config.passphraseFile` |\n\n## Resolution order (what the script actually does)\n\n**S3 credentials** (highest first):\n\n1. Process env `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` /\n   `AWS_SESSION_TOKEN` — operator-injected (systemd `EnvironmentFile=`,\n   gateway environment). Never overridden.\n2. **OpenClaw secret refs** — `config.accessKeyRef` + `config.secretKeyRef`\n   (+ optional `config.sessionTokenRef`). See \"Using the OpenClaw secret\n   store\" below. A configured-but-unresolvable ref aborts the run (exit 13) —\n   never a silent fallthrough to a weaker tier.\n3. `config.profile` → named profile in `~/.aws/credentials` (mode 600).\n   **Recommended simple default.** The skill passes `--profile`; the aws CLI\n   reads its own standard store. The secret never touches OpenClaw config or\n   this chat — and `~/.aws` is OUTSIDE the backup scope, so the key never\n   travels inside the archives it protects.\n4. DEPRECATED: `skills.entries.cloud-backup.env.ACCESS_KEY_ID` /\n   `.SECRET_ACCESS_KEY` / `.SESSION_TOKEN` plaintext in openclaw.json.\n   Still works in v2; warns loudly on every run; removed in v3.\n\n**GPG passphrase** (highest first):\n\n1. Process env `GPG_PASSPHRASE` — operator-injected.\n2. Process env `CLOUD_BACKUP_GPG_PASSPHRASE` — injected by OpenClaw from\n   `skills.entries.cloud-backup.apiKey` (which may itself be a SecretRef).\n   Resolves only inside the OpenClaw agent runtime, so a bare-shell\n   `cloud-backup.sh backup` won't see it — prefer `passphraseRef` below.\n3. **OpenClaw secret ref** — `config.passphraseRef` (below). Works from any\n   shell; aborts with exit 14 if configured but unresolvable.\n4. `config.passphraseFile` — path to a mode-600 file. **Recommended simple\n   default.** The script refuses world-readable files and warns on group\n   access. The passphrase is passed to gpg over a file descriptor — never on\n   a command line (v1 leaked it into `ps`/`/proc/*/cmdline`).\n5. DEPRECATED: `skills.entries.cloud-backup.env.GPG_PASSPHRASE` plaintext in\n   openclaw.json. Warns on every run; removed in v3.\n\n`status` always prints where each secret resolved from — check it whenever\nyou are unsure which tier is active.\n\n## Using the OpenClaw secret store (config.*Ref)\n\nIf you already run OpenClaw's secret system (`openclaw secrets configure`,\n`.secrets.providers` in openclaw.json), the skill hooks straight into it —\none credential model for the whole instance, 1Password-style backends\nincluded. The `*Ref` keys accept the same shapes OpenClaw uses everywhere:\na SecretRef object `{source, provider, id}` or a `$NAME` / `${NAME}` env\ntemplate.\n\n```json\n{ \"skills\": { \"entries\": { \"cloud-backup\": { \"config\": {\n  \"accessKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/accessKeyId\" },\n  \"secretKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/secretAccessKey\" },\n  \"passphraseRef\": { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/gpgPassphrase\" }\n} } } } }\n```\n\nSupported sources (resolved with the gateway's own semantics):\n\n- **file** — provider `{source: \"file\", path, mode: \"json\"|\"singleValue\"}`;\n  in json mode (the default) `id` is a JSON pointer into the chmod-600 store\n  file, e.g. `/cloudBackup/gpgPassphrase`.\n- **env** — `id` is the environment variable name.\n- **exec** — provider `{source: \"exec\", command, args?, jsonOnly?}`; the\n  skill speaks the protocolVersion-1 contract (request object on stdin,\n  `values` map on stdout), so the same resolver you use for the gateway —\n  e.g. a 1Password `op read` wrapper — works unchanged. Plugin-integration\n  exec providers resolve only inside the gateway and are rejected with a\n  clear error.\n\nNotes:\n\n- Configured refs that fail to resolve abort instead of falling back;\n  `status` shows `UNRESOLVABLE` with the reason.\n- Archive-scope trade-off: a file-provider store under `~/.openclaw` travels\n  inside every (encrypted) archive; an AWS profile never travels at all.\n  Both are fine — just know which you picked.\n- A literal secret string in a `*Ref` key technically works but is plaintext\n  in config — the sensitivity verdict will flag it. Use a real ref.\n\n## Generating a passphrase\n\n```bash\numask 077\nmkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n**Immediately store a copy in your password manager.** Without the\npassphrase, encrypted backups are unrecoverable — that is the point of them.\nIf you ever rotate it, keep the old one labeled with its date range: older\narchives still need it until they age out of retention.\n\n## Migrating from v1 (plaintext in openclaw.json)\n\n```bash\n# 1. S3 keys → profile (run yourself; do not paste keys into agent chat)\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n\n# 2. Passphrase → file (see above). If you keep the SAME passphrase, just move\n#    it; if you pick a new one, save BOTH in your password manager.\n\n# 3. Remove the plaintext block, then verify\nopenclaw config patch 'skills.entries.cloud-backup.env=null'\njq '.skills.entries[\"cloud-backup\"]' ~/.openclaw/openclaw.json   # no \"env\" block\nbash scripts/cloud-backup.sh status                              # no DEPRECATED warnings\n```\n\nConsider the old key exposed (it lived in config, which earlier backups\narchived): rotate it at the provider after the new chain is verified.\n\nFile v1.1.3:references/providers/aws-s3.md\n\n# AWS S3\n\n## 1. Create a private bucket\n\n1. AWS Console → S3 → **Create bucket**\n2. Keep **Block Public Access** enabled (all four checkboxes)\n3. Enable **Bucket Versioning** (recommended — protects against overwrites)\n4. Use **SSE-S3** encryption (default, free)\n\n## 2. Create a least-privilege key\n\nCreate a dedicated IAM user with programmatic access. Never use root keys.\nAttach this policy (replace `YOUR_BUCKET`):\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Sid\": \"ListBucket\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Sid\": \"ObjectAccess\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\nIAM → Users → Create user → attach policy → Create access key.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\nOn EC2/ECS, prefer an instance/task role and skip stored keys entirely.\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"YOUR_BUCKET\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n**No `endpoint`** — AWS S3 is the one provider where it stays unset.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Common regions: `us-east-1`, `us-west-2`, `eu-west-1`, `eu-central-1`.\n- If using S3 Object Lock or Glacier, extend the IAM policy accordingly.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/providers/backblaze-b2.md\n\n# Backblaze B2 (S3-compatible API)\n\n## 1. Create a private bucket\n\n1. Backblaze Console → **B2 Cloud Storage** → **Create a Bucket**\n2. Set to **Private**\n3. Disable Object Lock unless you need immutable backups\n\n## 2. Create a least-privilege key\n\n1. **App Keys** → **Add a New Application Key**\n2. **Restrict to your backup bucket**\n3. Allow: `listBuckets`, `listFiles`, `readFiles`, `writeFiles`, `deleteFiles`\n4. Note the **keyID** (= access key) and **applicationKey** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste keyID + applicationKey\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-west-004\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.us-west-004.backblazeb2.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nThe region is on the bucket details page and must match the endpoint.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Free tier: 10 GB storage, 1 GB/day egress.\n- Bucket-scoped application keys cannot list other buckets — that is the point.\n- Rotating the master key revokes ALL application keys.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/providers/cloudflare-r2.md\n\n# Cloudflare R2\n\n## 1. Create a private bucket\n\n1. Cloudflare Dashboard → **R2 Object Storage** → **Create bucket**\n2. Pick a name (lowercase, no dots); location hint \"Automatic\" is fine\n3. Recommended: add a lifecycle rule **Abort incomplete multipart uploads\n   after 1 day** (cleans up interrupted uploads)\n\n## 2. Create a least-privilege key\n\n1. R2 → **Manage R2 API Tokens** → **Create API token**\n2. Permissions: **Object Read & Write**\n3. Scope: **restrict to your backup bucket only**\n4. Note the **Access Key ID** and **Secret Access Key** for the next step\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste key id + secret; region: auto\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"auto\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://<ACCOUNT_ID>.r2.cloudflarestorage.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nFind your Account ID in the dashboard sidebar or the R2 overview page.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- R2 region is always `auto` — single global namespace.\n- Zero egress fees; free tier: 10 GB storage, 10M reads / 1M writes per month.\n- Signature errors usually mean a wrong Account ID in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/providers/digitalocean-spaces.md\n\n# DigitalOcean Spaces\n\n## 1. Create a private Space\n\n1. DigitalOcean Console → **Spaces Object Storage** → **Create a Space**\n2. Choose a datacenter region (`nyc3`, `sfo3`, `ams3`, `sgp1`, `fra1`, …)\n3. Restrict file listing: **Private**\n\n## 2. Create a key — read the warning first\n\n> **WARNING: Spaces keys are account-wide.** DigitalOcean does not support\n> per-Space scoping — this key can read, write, and delete EVERY Space in\n> your account. Prefer a dedicated team/project for backups, and rotate more\n> aggressively than usual (30–60 days). If account-wide blast radius is\n> unacceptable, pick a provider with bucket-scoped keys (R2, B2, AWS).\n\n1. **API** → **Spaces Keys** → **Generate New Key**\n2. Note the **Key** (= access key) and **Secret** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-space\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"nyc3\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://nyc3.digitaloceanspaces.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region in the endpoint and `config.region` must match.\n- No free tier (Spaces starts at $5/mo for 250 GB).\n- `SignatureDoesNotMatch` → endpoint region doesn't match the Space's region.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/providers/minio.md\n\n# MinIO\n\n## 1. Create a private bucket\n\n1. MinIO Console → **Buckets** → **Create Bucket**\n2. Set access to **Private**\n\n## 2. Create a least-privilege key\n\nNever use the admin credentials (`minioadmin`) for backups.\n\n1. MinIO Console → **Access Keys** → **Create Access Key**\n2. Attach a policy restricted to the one bucket:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Effect\": \"Allow\", \"Action\": [\"s3:ListBucket\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://minio.example.com:9000\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region can be anything; `us-east-1` is conventional.\n- Self-signed TLS: set `AWS_CA_BUNDLE=/path/to/ca.pem` in the environment.\n- Non-standard port goes in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/providers/other.md\n\n# Other / Custom S3-Compatible Provider\n\nAny storage service with an S3-compatible API works. You need three things:\na **private bucket**, the **S3 endpoint URL**, and a **key pair** — scoped to\nthe one bucket if your provider supports scoping (use it if so).\n\n## 1. Finding the endpoint\n\nAlways a full URL. Common patterns:\n\n- `https://s3.<region>.<provider>.com`\n- `https://<account-id>.r2.cloudflarestorage.com`\n- `https://<region>.digitaloceanspaces.com`\n- `https://minio.your-server.com:9000`\n\nLook for \"S3 API endpoint\" in your provider's compatibility docs.\n\n## 2. Create a least-privilege key\n\nLook for \"API keys\", \"access keys\", \"S3 credentials\", or \"application keys\"\nin the console. If the provider supports key scoping, restrict to the backup\nbucket with list/read/write/delete only.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.your-provider.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nIf unsure about region, use `us-east-1` or `auto`.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Verifying compatibility\n\nThe skill uses only basic operations: `aws s3 cp/ls/rm` plus a\n`head-object` check. No presigned URLs, no bucket creation, no ACLs. If\n`aws s3 ls s3://your-bucket/ --endpoint-url https://... --profile openclaw-backup`\nworks, you're good. (Custom object metadata is used for upload verification;\nproviders that drop it just skip the sha check, size is still verified.)\n\n## Troubleshooting\n\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch; try `region=auto`.\n- **SSL errors** — self-signed certs: `AWS_CA_BUNDLE=/path/to/ca.pem`.\n- **Connection refused** — include the port in the endpoint.\n- **Bucket-name DNS errors** — set `AWS_S3_FORCE_PATH_STYLE=true` in the env.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.3:references/security.md\n\n# Security — threat model, restore safety, incident response\n\n## Threat model (what the encryption is for)\n\nArchive encryption protects your backups **at rest in the bucket and in\nleak scenarios**: bucket compromise, provider insiders, a mislaid archive\nfile. It does **not** protect against host compromise — anyone with a shell\non your host already reads `~/.openclaw` directly.\n\nThat asymmetry drives the credential policy:\n\n- Host-side secrets in mode-600 files (`~/.aws/credentials`, the passphrase\n  file) are acceptable: they add no exposure beyond the host itself.\n- Secrets inside `openclaw.json` are NOT acceptable: **this skill archives\n  that file.** A credential stored there is replicated into every backup it\n  protects — exposure multiplied by retention count × destinations. This is\n  why v2 deprecates `skills.entries.cloud-backup.env.*` and why the docs\n  never suggest it.\n\n## Enforcement in the script (v2)\n\n- **Sensitivity verdict**: before every backup, the script detects whether\n  the archive scope contains real secret material (file-provider secret\n  stores, `credentials/`, legacy auth files, plaintext values in\n  openclaw.json). If it does, encryption is **forced** — plaintext output for\n  that scope fails with exit 14. `config.excludeSecrets=true` excludes the\n  detected stores instead (restores from such archives need secrets\n  re-provisioned — pair with an encrypted `backup settings`).\n- **No durable plaintext**: archives are produced inside a per-run mode-700\n  staging directory that is removed on any exit (and swept on the next run\n  after a hard kill). v1 could leave unencrypted tarballs behind when a step\n  failed; v2 cannot.\n- **No passphrase on command lines**: gpg receives the passphrase over a file\n  descriptor. v1 passed `--passphrase <value>` on argv, visible in\n  `ps`/`/proc/*/cmdline` for the duration of every run.\n- **Verified uploads**: sha256 sidecars, decrypt-and-list verification after\n  encryption, and a HEAD check (size + sha metadata) after upload.\n\n## Credential rules\n\n- Least-privilege, **bucket-scoped** keys (see the provider guides). Never\n  account-wide or root keys.\n- Storage: AWS named profile or operator-managed process env for S3; a\n  mode-600 passphrase file or OpenClaw SecretRef for GPG. Details and\n  resolution order: `references/credentials.md`.\n- Never commit credentials to git. `~/.openclaw/openclaw.json` should be\n  mode 600 regardless.\n- Rotate keys every ~90 days and immediately on any suspicion.\n\n## Restore safety\n\n1. Always `restore --dry-run` first — lists contents without extracting.\n2. Checksums are verified before anything else; decryption before listing.\n3. Tar member paths are validated — absolute paths and `..` traversal are\n   rejected before extraction.\n4. Restores are **staged by default** (a fresh mode-700 directory, with\n   printed next steps). `--in-place` overwrites live state and requires an\n   interactive typed confirmation, or `--yes --force` for automation.\n5. Cross-host caution: native archives record their original state dir; an\n   in-place restore onto a host with a different state dir is refused — go\n   through `--target`.\n\n## Troubleshooting\n\n- **`Unable to locate credentials`** — no profile/env configured. Set\n  `config.profile` (recommended) — see `references/credentials.md`.\n- **`AccessDenied`** — the key lacks `ListBucket` / `GetObject` / `PutObject`\n  / `DeleteObject` on the target bucket.\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch (check provider\n  guide), or system clock skew.\n- **`Could not connect to the endpoint URL`** — AWS: leave `endpoint` unset;\n  every other provider: `endpoint` is required.\n- **Checksum mismatch on restore** — re-download; if persistent, treat the\n  remote object as corrupted and restore an older set.\n- **exit 14** — the scope contains secret material and no passphrase is\n  configured. Set `config.passphraseFile`; do not bypass.\n\n## Incident response (key suspected leaked)\n\nRotate-first, revoke-last — never leave a window with zero working backups:\n\n1. Create a NEW bucket-scoped key in the provider console (old key stays\n   valid for now).\n2. Update the AWS profile (`aws configure --profile openclaw-backup`).\n3. Run `backup full` + `verify --latest` to prove the new chain works.\n4. Audit recent bucket activity for unexpected reads/writes/deletes.\n5. Revoke the OLD key.\n6. If the key ever lived in openclaw.json: archives made during that period\n   contain it. Prune them (`prune`, plus manual `aws s3 rm` for anything\n   outside retention) or treat the data they protect as exposed.\n7. If the passphrase leaked: pick a new one, keep the old labeled by date\n   range (old archives still need it), and re-create current backups under\n   the new passphrase.\n\nFile v1.1.3:references/setup-flow.md\n\n# First-time setup — guided flow (for the agent)\n\nPrinciples: the user understands every consequence before it happens; secrets\nnever transit the conversation; every config write is shown verbatim and\nconfirmed first; nothing is scheduled without an explicit opt-in.\n\n## Step 0 — Preflight\n\nRun `setup`. It prints the checklist, current config, dependency status, and\n(if cloud is configured) a connection test. It never writes anything.\n\n## Step 1 — Provider\n\nAsk which provider: AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean Spaces,\nMinIO, or another S3-compatible service. Read the matching\n`references/providers/<provider>.md`.\n\n## Step 2 — Bucket + least-privilege key\n\nEcho the provider guide's bucket and key steps. The user performs them in the\nprovider console: private bucket, then a key scoped to that one bucket (where\nthe provider supports scoping).\n\n## Step 3 — Credential home\n\nOffer exactly two options (never a third):\n\n1. **AWS profile (recommended)** — the user runs, themselves:\n   ```bash\n   aws configure --profile openclaw-backup\n   chmod 600 ~/.aws/credentials\n   ```\n   The key never appears in this chat.\n2. **Process env** — for operators who manage gateway env (systemd\n   `EnvironmentFile=`): `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`.\n\nPlaintext in openclaw.json is not offered. If the user asks for it, explain\nthe amplifier (backups archive the config) and point to\n`references/credentials.md`.\n\n## Step 4 — Config write (GATE)\n\nShow every patch verbatim, wait for an explicit yes, then run:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"<bucket>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"<endpoint>\"'   # non-AWS only\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"<region>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Step 5 — Connection test\n\nRun `setup` again (or `status`) and confirm \"Connected / Reachable ✓\".\n\n## Step 6 — Encryption (GATE on the config writes)\n\nTell the user:\n\n> \"Backups of this scope contain your OpenClaw config, credential store, and\n> agent databases, so encryption is required. Generate a strong passphrase\n> into a 600-mode file (run it yourself), then store a copy in your password\n> manager — without it, backups are unrecoverable:\"\n\n```bash\numask 077 && mkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\n```\n\nThen (after confirmation) set:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n(`config.encrypt` already defaults to true in v2.) Advanced alternative —\nSecretRef via `apiKey`: see `references/credentials.md`.\n\n## Step 7 — First backup (GATE)\n\n1. Run `backup full --dry-run`; show the user the scope, excludes, sensitivity\n   verdict, and destination.\n2. On yes: run `backup full`, then `verify --latest`.\n3. Report: archive name, size, encrypted ✓, uploaded ✓, verified ✓.\n\n## Step 8 — Scheduling offer (only now, only once)\n\nPer SKILL.md \"Scheduling — strictly opt-in\": offer once, show the exact\n`openclaw cron add` command from `schedule`, and create nothing without an\nexplicit yes. \"No\" is a complete answer — do not re-offer.\n\nFile v1.1.3:CHANGELOG.md\n\n# Changelog\n\n## 2.0.1 — 2026-06-11\n\n### Changed\n\n- Skill description polished (secrets-safe encryption, lean modes, opt-in\n  cron, staged restore) — the explicit-intent activation gate is unchanged.\n  No functional changes; identical script behavior to 2.0.0.\n\n## 2.0.0 — 2026-06-11\n\nComplete redesign: the skill is now **the cloud layer for OpenClaw's native\nbackup**, and was rebuilt around the ClawHub security-scan findings of v1.1.5\n(see SECURITY.md for the point-by-point map).\n\n### Breaking\n\n- The `full` engine is `openclaw backup create` (consistent SQLite snapshots,\n  embedded manifest, multi-directory coverage) plus a lean filter. The\n  `openclaw` CLI is therefore required for `backup full`.\n- `full` is **lean by default**: session transcripts, Codex caches/log\n  databases, `tools/`, `media/`, `logs/`, and old backups are excluded.\n  `--everything` (or `config.everything=true`) restores the old behavior.\n- `config.encrypt` now defaults to **true**, and encryption is **forced**\n  (exit 14 rather than a warning) whenever the archive scope contains real\n  secret material — detected automatically.\n- The local archive store moves from `~/.openclaw/backups` to\n  `~/.local/share/openclaw-backups` (`config.localDir`). Required: the native\n  engine refuses output inside a source path, and v1's location made every\n  new full backup swallow all previous ones.\n- `cleanup` renamed to `prune`; mode `skills` folded into `workspace` (both\n  aliases still work, with warnings).\n- Restores are **staged by default** (new directory + printed next steps);\n  in-place restores moved behind `--in-place` with typed confirmation\n  (`--yes --force` for automation).\n\n### Security (ClawHub scan remediation)\n\n- **SQP-1**: activation narrowed to explicit OpenClaw-backup intent;\n  SKILL.md adds per-action confirmation gates (config writes, first upload,\n  credential storage, restore, prune, schedule creation) and an\n  unattended-run policy.\n- **SQP-2 (cron)**: the daily job is no longer created by default.\n  Scheduling is opt-in, offered once after the first successful manual\n  backup, with the exact `openclaw cron add` command and payload shown\n  beforehand. The new `schedule` subcommand only prints.\n- **SQP-2 (credentials)**: all provider docs now lead with least-privilege\n  bucket-scoped keys in AWS named profiles or operator-managed env; the GPG\n  passphrase moves to a chmod-600 passphrase file or an OpenClaw SecretRef\n  (`apiKey` injected as `CLOUD_BACKUP_GPG_PASSPHRASE`); the passphrase is\n  passed to gpg via file descriptor, never argv; plaintext\n  `skills.entries.cloud-backup.env.*` still resolves but warns loudly on\n  every run.\n- Durable plaintext leftovers are structurally impossible: archives are\n  built in a per-run mode-700 staging dir, removed on any exit, swept after\n  hard kills. (v1 stranded unencrypted tarballs whenever a step failed\n  between tar and gpg.)\n\n### Added\n\n- **OpenClaw secret-store integration**: `config.accessKeyRef` /\n  `secretKeyRef` / `sessionTokenRef` / `passphraseRef` accept OpenClaw\n  SecretRefs (`{source: env|file|exec, provider, id}` or `$NAME` templates),\n  resolved against the instance's `.secrets.providers` with the gateway's own\n  semantics (JSON-pointer file stores, env vars, protocolVersion-1 exec\n  backends such as 1Password wrappers). Configured-but-unresolvable refs\n  abort the run (exit 13/14) and show as `UNRESOLVABLE` in `status` — never a\n  silent fallback to a weaker tier.\n- `verify [name|--latest] [--deep]` — checksum + decrypt + listing; `--deep`\n  also runs the native `openclaw backup verify` on the decrypted archive.\n- Automatic post-backup verification (`config.verifyAfterBackup`, default\n  `quick`: streamed decrypt + entry-count match) and post-upload HEAD\n  verification (size + sha256 metadata).\n- Sensitivity verdict (`refs-only` vs `secret-material`) shown in `status`\n  and `--dry-run`, driving the forced-encryption policy;\n  `config.excludeSecrets` to produce secret-free archives instead.\n- `config.exclude` / `config.include` (state-relative globs), `--no-upload`,\n  `--json`, `backup --dry-run` plan output.\n- `prune --dry-run` with exact delete plan; failure-debris detection\n  (plaintext leftovers, incomplete sets) in `list`/`status`/`prune`.\n- Per-mode retention (count + days) — a daily `settings` run can no longer\n  evict your `full` archives.\n- `flock` concurrency lock, disk-space and cloud-reachability preflights,\n  documented exit-code map for cron agents.\n- `restore --target DIR` (staged), `--only GLOB` (selective), `--latest`;\n  v1 archives remain fully restorable.\n- New references: `credentials.md` (resolution chain + migration),\n  `setup-flow.md` (guided, gated first-time setup).\n\n### Deprecated (removed in v3)\n\n- `skills.entries.cloud-backup.env.ACCESS_KEY_ID` / `SECRET_ACCESS_KEY` /\n  `SESSION_TOKEN` / `GPG_PASSPHRASE` — still work, warn on every run.\n- Command `cleanup`, mode `skills`.\n\n## 1.1.5 — 2026-02\n\nLast v1 release. See git history.\n\nArchive v1.1.2: 15 files, 46250 bytes\n\nFiles: CHANGELOG.md (4998b), references/credentials.md (6277b), references/providers/aws-s3.md (2432b), references/providers/backblaze-b2.md (2187b), references/providers/cloudflare-r2.md (2301b), references/providers/digitalocean-spaces.md (2370b), references/providers/minio.md (2215b), references/providers/other.md (2938b), references/security.md (4792b), references/setup-flow.md (3330b), scripts/cloud-backup.sh (63156b), SECURITY.md (4512b), skill-card.md (2691b), SKILL.md (9175b), _meta.json (140b)\n\nFile v1.1.2:SKILL.md\n\n---\nname: cloud-backup\ndescription: Disaster-proof OpenClaw: encrypted, verified backups to S3/R2/B2/MinIO, opt-in cron, easy restores. Use only when explicitly asked to back up/restore OpenClaw.\nmetadata: {\"openclaw\":{\"emoji\":\"☁️\",\"homepage\":\"https://github.com/obuchowski/openclaw-cloud-backup\",\"os\":[\"linux\",\"darwin\"],\"requires\":{\"bins\":[\"bash\",\"tar\",\"jq\"]},\"install\":[{\"kind\":\"brew\",\"formula\":\"awscli\",\"bins\":[\"aws\"]},{\"kind\":\"brew\",\"formula\":\"gnupg\",\"bins\":[\"gpg\"]}],\"primaryEnv\":\"CLOUD_BACKUP_GPG_PASSPHRASE\"}}\n---\n\n# OpenClaw Cloud Backup\n\nThe cloud layer for OpenClaw's native backup. Wraps `openclaw backup create`\n(config, credentials, consistent SQLite snapshots, workspace), then GPG-encrypts\nand uploads the archive to any S3-compatible bucket, with retention,\nverification, and staged restore.\n\nAll commands: `bash \"{baseDir}/scripts/cloud-backup.sh\" <subcommand>`\n\n## When to use this skill — and when not to\n\nAct ONLY on an explicit user request about OpenClaw backups: \"back up\nopenclaw\", \"restore my openclaw state\", \"set up cloud backups for openclaw\",\n\"/cloud-backup\", and similar.\n\n- Generic requests (\"back up my project\", \"save this file\", \"restore the\n  database\") are NOT for this skill. Ask what the user means before touching it.\n- NEVER run this skill as a side effect of another task, proactively, or\n  \"while you're at it\".\n- Read-only subcommands (`status`, `list`, `verify`, `schedule`, and any\n  `--dry-run`) may run freely once the user has asked about backups.\n  Everything else follows the gates below.\n\n## Confirmation gates\n\nBefore ANY state-changing action, show the user exactly what will happen and\nget an explicit yes. One gate per action — do not re-ask for things the user\njust confirmed, and never batch-confirm.\n\n| Action | What you MUST show before doing it |\n|---|---|\n| Write config (`openclaw config patch`) | Every key=value you will write, verbatim. Never write secrets — see Credentials. |\n| First backup to a new destination | Output of `backup <mode> --dry-run`: scope, sensitivity verdict, encryption status, target `s3://bucket/prefix`. |\n| Store/replace a credential | Only the file path + storage method (AWS profile / passphrase file). The secret value itself should not transit this conversation — the user runs those commands themselves. |\n| Restore | Step 1: always `restore <name> --dry-run` and show the file list. Step 2: state which paths will be overwritten (staged restores write to a fresh directory; `--in-place` overwrites live state), then require an explicit yes. Never skip the dry run. |\n| Prune | Output of `prune --dry-run`: which archives (local and remote) will be deleted, by name. |\n| Schedule creation | The exact `openclaw cron add ...` command and full payload text (see Scheduling). |\n\nRepeat manual backups to an already-confirmed destination need no new gate —\nthe user's request IS the confirmation. Still echo the one-line plan\n(\"full backup, encrypted, → s3://bucket/prefix\") before running.\n\n### Unattended runs (cron)\n\nA scheduled job's payload marks the run as operator-preconfirmed for `backup`\nand `prune` ONLY. In unattended runs: never restore, never change config,\nnever create or modify schedules, never store credentials.\n\n## Modes — what each backup contains\n\nEcho this table when the user asks what gets backed up:\n\n| Mode | Includes | Excludes by default | Sensitivity |\n|---|---|---|---|\n| `backup full` (default) | openclaw.json, credentials/, secret stores, state + agent SQLite snapshots, agent memory, workspace, installed skills | session transcripts, codex caches/logs, tools/, media/, logs/, old backups | SENSITIVE — encryption forced |\n| `backup full --everything` | everything above PLUS session transcripts and codex history | previous backup archives only | SENSITIVE — encryption forced |\n| `backup settings` | openclaw.json, secret stores, credentials/, auth files | everything else | SENSITIVE — always encrypted, no opt-out |\n| `backup workspace` | workspace directories (skills, memory files) | all state/config | encrypted by default; `--no-encrypt` allowed here only |\n\n\"SENSITIVE — encryption forced\" means the script refuses to produce a plaintext\narchive for that scope (exit 14). Do not work around it; if the user explicitly\nwants a plaintext-shareable archive, offer `config.excludeSecrets=true` instead.\nUsers can tune scope with `config.exclude` / `config.include` (state-relative\nglobs).\n\n## Subcommands\n\n| Command | What it does | Gate? |\n|---|---|---|\n| `backup [full\\|settings\\|workspace] [--everything] [--no-upload] [--dry-run] [--json]` | Create archive, encrypt, upload, apply retention | dry-run free; see gates |\n| `list` | Local + remote backups; flags failure debris | no |\n| `status` | Health: last backup, credential sources, sensitivity verdict, schedule, reachability | no |\n| `verify [name\\|--latest] [--deep]` | Checksum + decrypt + listing; `--deep` adds `openclaw backup verify` | no |\n| `restore <name\\|--latest> [--target DIR \\| --in-place] [--only GLOB] [--dry-run] [--yes] [--force]` | Staged restore by default | YES — two-step |\n| `prune [--dry-run]` | Apply retention; remove failure debris | YES |\n| `schedule` | PRINT the opt-in cron command (creates nothing) | no |\n| `setup` | Setup checklist + connection test (never writes config) | config writes gated individually |\n\nExit codes (report them precisely, especially from cron): 0 ok ·\n3 ok-with-warnings · 4 usage · 10 another run holds the lock · 11 missing\ndependency · 12 insufficient disk · 13 cloud unreachable/bad credentials ·\n14 encryption required but unavailable · 20-25 create/filter/encrypt/upload/\nverify failures · 30 restore failure.\n\n## First-time setup\n\nFollow `references/setup-flow.md` step by step. Summary: choose provider →\nuser creates a least-privilege bucket-scoped key (per provider guide) → store\ncredentials OUTSIDE OpenClaw config (AWS profile recommended) → write\nnon-secret config (gate) → test connection → enable encryption with a\ngenerated passphrase file → first manual backup (gate) → only then offer\nscheduling (gate).\n\n## Credentials\n\nResolution order and storage rules: `references/credentials.md`. Hard rules:\n\n- NEVER write access keys or passphrases into openclaw.json (no\n  `skills.entries.cloud-backup.env.*`). Backups archive that file: a plaintext\n  credential in config is carried inside every archive it protects.\n- S3 credentials live in an AWS named profile (`config.profile`, recommended)\n  or operator-managed process env. The passphrase lives in a chmod-600\n  passphrase file (`config.passphraseFile`).\n- Operators who run OpenClaw's secret store can point the skill at it\n  instead: `config.accessKeyRef` / `config.secretKeyRef` /\n  `config.passphraseRef` accept OpenClaw SecretRefs ({source: env|file|exec})\n  resolved against `.secrets.providers` — including 1Password-style exec\n  providers. Configured-but-broken refs abort the run; they never fall back.\n- Prefer flows where the secret never appears in this conversation: the user\n  runs `aws configure --profile ...` and the passphrase generator themselves.\n- If the script prints a DEPRECATED warning about plaintext config\n  credentials, surface it to the user verbatim and offer the migration in\n  `references/credentials.md`.\n\n## Scheduling — strictly opt-in\n\nNEVER create, modify, or enable a cron job by default, implicitly, or because\n\"backups should be scheduled\". Skills document cron setup; only the user\nopts in.\n\nOffer scheduling exactly once, AFTER the first successful manual backup:\n\n> \"Backup verified. Want me to schedule this daily? I'd create this cron job —\n> nothing is scheduled until you confirm:\"\n\nThen run `schedule` to print the exact command, adjust time/timezone/delivery\nto the user's setup, show it in full, and wait for an explicit yes before\nrunning it. If the user declines: drop it, and do not re-offer on later runs\n(check `openclaw cron list` first — if a cloud-backup job exists, never offer).\nTo remove a schedule: `openclaw cron rm <id>`.\n\n## Error handling (for you, the agent)\n\n- Surface script stderr to the user verbatim (trim to the relevant lines).\n  Never summarize an error into vagueness, never hide a WARN.\n- Non-zero exit: diagnose using the exit-code table, explain, propose ONE fix.\n  Never auto-retry `restore`, `prune`, or any `openclaw cron` mutation.\n  `backup` may be retried once, only after the cause is fixed and the user\n  agrees.\n- Never invent bucket names, endpoints, or passphrases. Missing value → ask.\n- Exit 14 (encryption required, no passphrase) fails hard by design — do not\n  work around it with `--no-encrypt` or an ad-hoc passphrase. Run setup.\n- The script cleans its staging on failure; if it reports it could not, tell\n  the user the exact leftover path.\n\n## Reference docs (read only when needed)\n\n- `references/credentials.md` — resolution chain, v1→v2 migration, warnings\n- `references/setup-flow.md` — guided first-time setup\n- `references/providers/{aws-s3,cloudflare-r2,backblaze-b2,digitalocean-spaces,minio,other}.md`\n- `references/security.md` — threat model, restore safety, incident response\n\nFile v1.1.2:_meta.json\n\n{\n  \"ownerId\": \"kn73d89b2a038m7cxgqww1w5ah81a7ar\",\n  \"slug\": \"openclaw-cloud-backup\",\n  \"version\": \"1.1.2\",\n  \"publishedAt\": 1781217585254\n}\n\nFile v1.1.2:references/credentials.md\n\n# Credentials — where each secret lives, and why\n\nThis skill needs up to three secrets: an S3 access key id, an S3 secret key,\nand a GPG passphrase. **None of them belong in openclaw.json.** Backups\narchive that file — a credential stored there rides along inside every\narchive it protects.\n\nThe recommended setup is two chmod-600 files:\n\n| Secret | Home | How |\n|---|---|---|\n| S3 key pair | AWS named profile (`~/.aws/credentials`) | `aws configure --profile openclaw-backup && chmod 600 ~/.aws/credentials`, then set `config.profile` |\n| GPG passphrase | passphrase file | `umask 077 && openssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase`, then set `config.passphraseFile` |\n\n## Resolution order (what the script actually does)\n\n**S3 credentials** (highest first):\n\n1. Process env `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` /\n   `AWS_SESSION_TOKEN` — operator-injected (systemd `EnvironmentFile=`,\n   gateway environment). Never overridden.\n2. **OpenClaw secret refs** — `config.accessKeyRef` + `config.secretKeyRef`\n   (+ optional `config.sessionTokenRef`). See \"Using the OpenClaw secret\n   store\" below. A configured-but-unresolvable ref aborts the run (exit 13) —\n   never a silent fallthrough to a weaker tier.\n3. `config.profile` → named profile in `~/.aws/credentials` (mode 600).\n   **Recommended simple default.** The skill passes `--profile`; the aws CLI\n   reads its own standard store. The secret never touches OpenClaw config or\n   this chat — and `~/.aws` is OUTSIDE the backup scope, so the key never\n   travels inside the archives it protects.\n4. DEPRECATED: `skills.entries.cloud-backup.env.ACCESS_KEY_ID` /\n   `.SECRET_ACCESS_KEY` / `.SESSION_TOKEN` plaintext in openclaw.json.\n   Still works in v2; warns loudly on every run; removed in v3.\n\n**GPG passphrase** (highest first):\n\n1. Process env `GPG_PASSPHRASE` — operator-injected.\n2. Process env `CLOUD_BACKUP_GPG_PASSPHRASE` — injected by OpenClaw from\n   `skills.entries.cloud-backup.apiKey` (which may itself be a SecretRef).\n   Resolves only inside the OpenClaw agent runtime, so a bare-shell\n   `cloud-backup.sh backup` won't see it — prefer `passphraseRef` below.\n3. **OpenClaw secret ref** — `config.passphraseRef` (below). Works from any\n   shell; aborts with exit 14 if configured but unresolvable.\n4. `config.passphraseFile` — path to a mode-600 file. **Recommended simple\n   default.** The script refuses world-readable files and warns on group\n   access. The passphrase is passed to gpg over a file descriptor — never on\n   a command line (v1 leaked it into `ps`/`/proc/*/cmdline`).\n5. DEPRECATED: `skills.entries.cloud-backup.env.GPG_PASSPHRASE` plaintext in\n   openclaw.json. Warns on every run; removed in v3.\n\n`status` always prints where each secret resolved from — check it whenever\nyou are unsure which tier is active.\n\n## Using the OpenClaw secret store (config.*Ref)\n\nIf you already run OpenClaw's secret system (`openclaw secrets configure`,\n`.secrets.providers` in openclaw.json), the skill hooks straight into it —\none credential model for the whole instance, 1Password-style backends\nincluded. The `*Ref` keys accept the same shapes OpenClaw uses everywhere:\na SecretRef object `{source, provider, id}` or a `$NAME` / `${NAME}` env\ntemplate.\n\n```json\n{ \"skills\": { \"entries\": { \"cloud-backup\": { \"config\": {\n  \"accessKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/accessKeyId\" },\n  \"secretKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/secretAccessKey\" },\n  \"passphraseRef\": { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/gpgPassphrase\" }\n} } } } }\n```\n\nSupported sources (resolved with the gateway's own semantics):\n\n- **file** — provider `{source: \"file\", path, mode: \"json\"|\"singleValue\"}`;\n  in json mode (the default) `id` is a JSON pointer into the chmod-600 store\n  file, e.g. `/cloudBackup/gpgPassphrase`.\n- **env** — `id` is the environment variable name.\n- **exec** — provider `{source: \"exec\", command, args?, jsonOnly?}`; the\n  skill speaks the protocolVersion-1 contract (request object on stdin,\n  `values` map on stdout), so the same resolver you use for the gateway —\n  e.g. a 1Password `op read` wrapper — works unchanged. Plugin-integration\n  exec providers resolve only inside the gateway and are rejected with a\n  clear error.\n\nNotes:\n\n- Configured refs that fail to resolve abort instead of falling back;\n  `status` shows `UNRESOLVABLE` with the reason.\n- Archive-scope trade-off: a file-provider store under `~/.openclaw` travels\n  inside every (encrypted) archive; an AWS profile never travels at all.\n  Both are fine — just know which you picked.\n- A literal secret string in a `*Ref` key technically works but is plaintext\n  in config — the sensitivity verdict will flag it. Use a real ref.\n\n## Generating a passphrase\n\n```bash\numask 077\nmkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n**Immediately store a copy in your password manager.** Without the\npassphrase, encrypted backups are unrecoverable — that is the point of them.\nIf you ever rotate it, keep the old one labeled with its date range: older\narchives still need it until they age out of retention.\n\n## Migrating from v1 (plaintext in openclaw.json)\n\n```bash\n# 1. S3 keys → profile (run yourself; do not paste keys into agent chat)\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n\n# 2. Passphrase → file (see above). If you keep the SAME passphrase, just move\n#    it; if you pick a new one, save BOTH in your password manager.\n\n# 3. Remove the plaintext block, then verify\nopenclaw config patch 'skills.entries.cloud-backup.env=null'\njq '.skills.entries[\"cloud-backup\"]' ~/.openclaw/openclaw.json   # no \"env\" block\nbash scripts/cloud-backup.sh status                              # no DEPRECATED warnings\n```\n\nConsider the old key exposed (it lived in config, which earlier backups\narchived): rotate it at the provider after the new chain is verified.\n\nFile v1.1.2:references/providers/aws-s3.md\n\n# AWS S3\n\n## 1. Create a private bucket\n\n1. AWS Console → S3 → **Create bucket**\n2. Keep **Block Public Access** enabled (all four checkboxes)\n3. Enable **Bucket Versioning** (recommended — protects against overwrites)\n4. Use **SSE-S3** encryption (default, free)\n\n## 2. Create a least-privilege key\n\nCreate a dedicated IAM user with programmatic access. Never use root keys.\nAttach this policy (replace `YOUR_BUCKET`):\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Sid\": \"ListBucket\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Sid\": \"ObjectAccess\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\nIAM → Users → Create user → attach policy → Create access key.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\nOn EC2/ECS, prefer an instance/task role and skip stored keys entirely.\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"YOUR_BUCKET\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n**No `endpoint`** — AWS S3 is the one provider where it stays unset.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Common regions: `us-east-1`, `us-west-2`, `eu-west-1`, `eu-central-1`.\n- If using S3 Object Lock or Glacier, extend the IAM policy accordingly.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/providers/backblaze-b2.md\n\n# Backblaze B2 (S3-compatible API)\n\n## 1. Create a private bucket\n\n1. Backblaze Console → **B2 Cloud Storage** → **Create a Bucket**\n2. Set to **Private**\n3. Disable Object Lock unless you need immutable backups\n\n## 2. Create a least-privilege key\n\n1. **App Keys** → **Add a New Application Key**\n2. **Restrict to your backup bucket**\n3. Allow: `listBuckets`, `listFiles`, `readFiles`, `writeFiles`, `deleteFiles`\n4. Note the **keyID** (= access key) and **applicationKey** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste keyID + applicationKey\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-west-004\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.us-west-004.backblazeb2.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nThe region is on the bucket details page and must match the endpoint.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Free tier: 10 GB storage, 1 GB/day egress.\n- Bucket-scoped application keys cannot list other buckets — that is the point.\n- Rotating the master key revokes ALL application keys.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/providers/cloudflare-r2.md\n\n# Cloudflare R2\n\n## 1. Create a private bucket\n\n1. Cloudflare Dashboard → **R2 Object Storage** → **Create bucket**\n2. Pick a name (lowercase, no dots); location hint \"Automatic\" is fine\n3. Recommended: add a lifecycle rule **Abort incomplete multipart uploads\n   after 1 day** (cleans up interrupted uploads)\n\n## 2. Create a least-privilege key\n\n1. R2 → **Manage R2 API Tokens** → **Create API token**\n2. Permissions: **Object Read & Write**\n3. Scope: **restrict to your backup bucket only**\n4. Note the **Access Key ID** and **Secret Access Key** for the next step\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste key id + secret; region: auto\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"auto\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://<ACCOUNT_ID>.r2.cloudflarestorage.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nFind your Account ID in the dashboard sidebar or the R2 overview page.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- R2 region is always `auto` — single global namespace.\n- Zero egress fees; free tier: 10 GB storage, 10M reads / 1M writes per month.\n- Signature errors usually mean a wrong Account ID in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/providers/digitalocean-spaces.md\n\n# DigitalOcean Spaces\n\n## 1. Create a private Space\n\n1. DigitalOcean Console → **Spaces Object Storage** → **Create a Space**\n2. Choose a datacenter region (`nyc3`, `sfo3`, `ams3`, `sgp1`, `fra1`, …)\n3. Restrict file listing: **Private**\n\n## 2. Create a key — read the warning first\n\n> **WARNING: Spaces keys are account-wide.** DigitalOcean does not support\n> per-Space scoping — this key can read, write, and delete EVERY Space in\n> your account. Prefer a dedicated team/project for backups, and rotate more\n> aggressively than usual (30–60 days). If account-wide blast radius is\n> unacceptable, pick a provider with bucket-scoped keys (R2, B2, AWS).\n\n1. **API** → **Spaces Keys** → **Generate New Key**\n2. Note the **Key** (= access key) and **Secret** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-space\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"nyc3\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://nyc3.digitaloceanspaces.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region in the endpoint and `config.region` must match.\n- No free tier (Spaces starts at $5/mo for 250 GB).\n- `SignatureDoesNotMatch` → endpoint region doesn't match the Space's region.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/providers/minio.md\n\n# MinIO\n\n## 1. Create a private bucket\n\n1. MinIO Console → **Buckets** → **Create Bucket**\n2. Set access to **Private**\n\n## 2. Create a least-privilege key\n\nNever use the admin credentials (`minioadmin`) for backups.\n\n1. MinIO Console → **Access Keys** → **Create Access Key**\n2. Attach a policy restricted to the one bucket:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Effect\": \"Allow\", \"Action\": [\"s3:ListBucket\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://minio.example.com:9000\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Region can be anything; `us-east-1` is conventional.\n- Self-signed TLS: set `AWS_CA_BUNDLE=/path/to/ca.pem` in the environment.\n- Non-standard port goes in the endpoint URL.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/providers/other.md\n\n# Other / Custom S3-Compatible Provider\n\nAny storage service with an S3-compatible API works. You need three things:\na **private bucket**, the **S3 endpoint URL**, and a **key pair** — scoped to\nthe one bucket if your provider supports scoping (use it if so).\n\n## 1. Finding the endpoint\n\nAlways a full URL. Common patterns:\n\n- `https://s3.<region>.<provider>.com`\n- `https://<account-id>.r2.cloudflarestorage.com`\n- `https://<region>.digitaloceanspaces.com`\n- `https://minio.your-server.com:9000`\n\nLook for \"S3 API endpoint\" in your provider's compatibility docs.\n\n## 2. Create a least-privilege key\n\nLook for \"API keys\", \"access keys\", \"S3 credentials\", or \"application keys\"\nin the console. If the provider supports key scoping, restrict to the backup\nbucket with list/read/write/delete only.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.your-provider.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nIf unsure about region, use `us-east-1` or `auto`.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Verifying compatibility\n\nThe skill uses only basic operations: `aws s3 cp/ls/rm` plus a\n`head-object` check. No presigned URLs, no bucket creation, no ACLs. If\n`aws s3 ls s3://your-bucket/ --endpoint-url https://... --profile openclaw-backup`\nworks, you're good. (Custom object metadata is used for upload verification;\nproviders that drop it just skip the sha check, size is still verified.)\n\n## Troubleshooting\n\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch; try `region=auto`.\n- **SSL errors** — self-signed certs: `AWS_CA_BUNDLE=/path/to/ca.pem`.\n- **Connection refused** — include the port in the endpoint.\n- **Bucket-name DNS errors** — set `AWS_S3_FORCE_PATH_STYLE=true` in the env.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`.\n\nFile v1.1.2:references/security.md\n\n# Security — threat model, restore safety, incident response\n\n## Threat model (what the encryption is for)\n\nArchive encryption protects your backups **at rest in the bucket and in\nleak scenarios**: bucket compromise, provider insiders, a mislaid archive\nfile. It does **not** protect against host compromise — anyone with a shell\non your host already reads `~/.openclaw` directly.\n\nThat asymmetry drives the credential policy:\n\n- Host-side secrets in mode-600 files (`~/.aws/credentials`, the passphrase\n  file) are acceptable: they add no exposure beyond the host itself.\n- Secrets inside `openclaw.json` are NOT acceptable: **this skill archives\n  that file.** A credential stored there is replicated into every backup it\n  protects — exposure multiplied by retention count × destinations. This is\n  why v2 deprecates `skills.entries.cloud-backup.env.*` and why the docs\n  never suggest it.\n\n## Enforcement in the script (v2)\n\n- **Sensitivity verdict**: before every backup, the script detects whether\n  the archive scope contains real secret material (file-provider secret\n  stores, `credentials/`, legacy auth files, plaintext values in\n  openclaw.json). If it does, encryption is **forced** — plaintext output for\n  that scope fails with exit 14. `config.excludeSecrets=true` excludes the\n  detected stores instead (restores from such archives need secrets\n  re-provisioned — pair with an encrypted `backup settings`).\n- **No durable plaintext**: archives are produced inside a per-run mode-700\n  staging directory that is removed on any exit (and swept on the next run\n  after a hard kill). v1 could leave unencrypted tarballs behind when a step\n  failed; v2 cannot.\n- **No passphrase on command lines**: gpg receives the passphrase over a file\n  descriptor. v1 passed `--passphrase <value>` on argv, visible in\n  `ps`/`/proc/*/cmdline` for the duration of every run.\n- **Verified uploads**: sha256 sidecars, decrypt-and-list verification after\n  encryption, and a HEAD check (size + sha metadata) after upload.\n\n## Credential rules\n\n- Least-privilege, **bucket-scoped** keys (see the provider guides). Never\n  account-wide or root keys.\n- Storage: AWS named profile or operator-managed process env for S3; a\n  mode-600 passphrase file or OpenClaw SecretRef for GPG. Details and\n  resolution order: `references/credentials.md`.\n- Never commit credentials to git. `~/.openclaw/openclaw.json` should be\n  mode 600 regardless.\n- Rotate keys every ~90 days and immediately on any suspicion.\n\n## Restore safety\n\n1. Always `restore --dry-run` first — lists contents without extracting.\n2. Checksums are verified before anything else; decryption before listing.\n3. Tar member paths are validated — absolute paths and `..` traversal are\n   rejected before extraction.\n4. Restores are **staged by default** (a fresh mode-700 directory, with\n   printed next steps). `--in-place` overwrites live state and requires an\n   interactive typed confirmation, or `--yes --force` for automation.\n5. Cross-host caution: native archives record their original state dir; an\n   in-place restore onto a host with a different state dir is refused — go\n   through `--target`.\n\n## Troubleshooting\n\n- **`Unable to locate credentials`** — no profile/env configured. Set\n  `config.profile` (recommended) — see `references/credentials.md`.\n- **`AccessDenied`** — the key lacks `ListBucket` / `GetObject` / `PutObject`\n  / `DeleteObject` on the target bucket.\n- **`SignatureDoesNotMatch`** — region/endpoint mismatch (check provider\n  guide), or system clock skew.\n- **`Could not connect to the endpoint URL`** — AWS: leave `endpoint` unset;\n  every other provider: `endpoint` is required.\n- **Checksum mismatch on restore** — re-download; if persistent, treat the\n  remote object as corrupted and restore an older set.\n- **exit 14** — the scope contains secret material and no passphrase is\n  configured. Set `config.passphraseFile`; do not bypass.\n\n## Incident response (key suspected leaked)\n\nRotate-first, revoke-last — never leave a window with zero working backups:\n\n1. Create a NEW bucket-scoped key in the provider console (old key stays\n   valid for now).\n2. Update the AWS profile (`aws configure --profile openclaw-backup`).\n3. Run `backup full` + `verify --latest` to prove the new chain works.\n4. Audit recent bucket activity for unexpected reads/writes/deletes.\n5. Revoke the OLD key.\n6. If the key ever lived in openclaw.json: archives made during that period\n   contain it. Prune them (`prune`, plus manual `aws s3 rm` for anything\n   outside retention) or treat the data they protect as exposed.\n7. If the passphrase leaked: pick a new one, keep the old labeled by date\n   range (old archives still need it), and re-create current backups under\n   the new passphrase.\n\nFile v1.1.2:references/setup-flow.md\n\n# First-time setup — guided flow (for the agent)\n\nPrinciples: the user understands every consequence before it happens; secrets\nnever transit the conversation; every config write is shown verbatim and\nconfirmed first; nothing is scheduled without an explicit opt-in.\n\n## Step 0 — Preflight\n\nRun `setup`. It prints the checklist, current config, dependency status, and\n(if cloud is configured) a connection test. It never writes anything.\n\n## Step 1 — Provider\n\nAsk which provider: AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean Spaces,\nMinIO, or another S3-compatible service. Read the matching\n`references/providers/<provider>.md`.\n\n## Step 2 — Bucket + least-privilege key\n\nEcho the provider guide's bucket and key steps. The user performs them in the\nprovider console: private bucket, then a key scoped to that one bucket (where\nthe provider supports scoping).\n\n## Step 3 — Credential home\n\nOffer exactly two options (never a third):\n\n1. **AWS profile (recommended)** — the user runs, themselves:\n   ```bash\n   aws configure --profile openclaw-backup\n   chmod 600 ~/.aws/credentials\n   ```\n   The key never appears in this chat.\n2. **Process env** — for operators who manage gateway env (systemd\n   `EnvironmentFile=`): `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`.\n\nPlaintext in openclaw.json is not offered. If the user asks for it, explain\nthe amplifier (backups archive the config) and point to\n`references/credentials.md`.\n\n## Step 4 — Config write (GATE)\n\nShow every patch verbatim, wait for an explicit yes, then run:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"<bucket>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"<endpoint>\"'   # non-AWS only\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"<region>\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n## Step 5 — Connection test\n\nRun `setup` again (or `status`) and confirm \"Connected / Reachable ✓\".\n\n## Step 6 — Encryption (GATE on the config writes)\n\nTell the user:\n\n> \"Backups of this scope contain your OpenClaw config, credential store, and\n> agent databases, so encryption is required. Generate a strong passphrase\n> into a 600-mode file (run it yourself), then store a copy in your password\n> manager — without it, backups are unrecoverable:\"\n\n```bash\numask 077 && mkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\n```\n\nThen (after confirmation) set:\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'\n```\n\n(`config.encrypt` already defaults to true in v2.) Advanced alternative —\nSecretRef via `apiKey`: see `references/credentials.md`.\n\n## Step 7 — First backup (GATE)\n\n1. Run `backup full --dry-run`; show the user the scope, excludes, sensitivity\n   verdict, and destination.\n2. On yes: run `backup full`, then `verify --latest`.\n3. Report: archive name, size, encrypted ✓, uploaded ✓, verified ✓.\n\n## Step 8 — Scheduling offer (only now, only once)\n\nPer SKILL.md \"Scheduling — strictly opt-in\": offer once, show the exact\n`openclaw cron add` command from `schedule`, and create nothing without an\nexplicit yes. \"No\" is a complete answer — do not re-offer.\n\nFile v1.1.2:CHANGELOG.md\n\n# Changelog\n\n## 2.0.1 — 2026-06-11\n\n### Changed\n\n- Skill description polished (secrets-safe encryption, lean modes, opt-in\n  cron, staged restore) — the explicit-intent activation gate is unchanged.\n  No functional changes; identical script behavior to 2.0.0.\n\n## 2.0.0 — 2026-06-11\n\nComplete redesign: the skill is now **the cloud layer for OpenClaw's native\nbackup**, and was rebuilt around the ClawHub security-scan findings of v1.1.5\n(see SECURITY.md for the point-by-point map).\n\n### Breaking\n\n- The `full` engine is `openclaw backup create` (consistent SQLite snapshots,\n  embedded manifest, multi-directory coverage) plus a lean filter. The\n  `openclaw` CLI is therefore required for `backup full`.\n- `full` is **lean by default**: session transcripts, Codex caches/log\n  databases, `tools/`, `media/`, `logs/`, and old backups are excluded.\n  `--everything` (or `config.everything=true`) restores the old behavior.\n- `config.encrypt` now defaults to **true**, and encryption is **forced**\n  (exit 14 rather than a warning) whenever the archive scope contains real\n  secret material — detected automatically.\n- The local archive store moves from `~/.openclaw/backups` to\n  `~/.local/share/openclaw-backups` (`config.localDir`). Required: the native\n  engine refuses output inside a source path, and v1's location made every\n  new full backup swallow all previous ones.\n- `cleanup` renamed to `prune`; mode `skills` folded into `workspace` (both\n  aliases still work, with warnings).\n- Restores are **staged by default** (new directory + printed next steps);\n  in-place restores moved behind `--in-place` with typed confirmation\n  (`--yes --force` for automation).\n\n### Security (ClawHub scan remediation)\n\n- **SQP-1**: activation narrowed to explicit OpenClaw-backup intent;\n  SKILL.md adds per-action confirmation gates (config writes, first upload,\n  credential storage, restore, prune, schedule creation) and an\n  unattended-run policy.\n- **SQP-2 (cron)**: the daily job is no longer created by default.\n  Scheduling is opt-in, offered once after the first successful manual\n  backup, with the exact `openclaw cron add` command and payload shown\n  beforehand. The new `schedule` subcommand only prints.\n- **SQP-2 (credentials)**: all provider docs now lead with least-privilege\n  bucket-scoped keys in AWS named profiles or operator-managed env; the GPG\n  passphrase moves to a chmod-600 passphrase file or an OpenClaw SecretRef\n  (`apiKey` injected as `CLOUD_BACKUP_GPG_PASSPHRASE`); the passphrase is\n  passed to gpg via file descriptor, never argv; plaintext\n  `skills.entries.cloud-backup.env.*` still resolves but warns loudly on\n  every run.\n- Durable plaintext leftovers are structurally impossible: archives are\n  built in a per-run mode-700 staging dir, removed on any exit, swept after\n  hard kills. (v1 stranded unencrypted tarballs whenever a step failed\n  between tar and gpg.)\n\n### Added\n\n- **OpenClaw secret-store integration**: `config.accessKeyRef` /\n  `secretKeyRef` / `sessionTokenRef` / `passphraseRef` accept OpenClaw\n  SecretRefs (`{source: env|file|exec, provider, id}` or `$NAME` templates),\n  resolved against the instance's `.secrets.providers` with the gateway's own\n  semantics (JSON-pointer file stores, env vars, protocolVersion-1 exec\n  backends such as 1Password wrappers). Configured-but-unresolvable refs\n  abort the run (exit 13/14) and show as `UNRESOLVABLE` in `status` — never a\n  silent fallback to a weaker tier.\n- `verify [name|--latest] [--deep]` — checksum + decrypt + listing; `--deep`\n  also runs the native `openclaw backup verify` on the decrypted archive.\n- Automatic post-backup verification (`config.verifyAfterBackup`, default\n  `quick`: streamed decrypt + entry-count match) and post-upload HEAD\n  verification (size + sha256 metadata).\n- Sensitivity verdict (`refs-only` vs `secret-material`) shown in `status`\n  and `--dry-run`, driving the forced-encryption policy;\n  `config.excludeSecrets` to produce secret-free archives instead.\n- `config.exclude` / `config.include` (state-relative globs), `--no-upload`,\n  `--json`, `backup --dry-run` plan output.\n- `prune --dry-run` with exact delete plan; failure-debris detection\n  (plaintext leftovers, incomplete sets) in `list`/`status`/`prune`.\n- Per-mode retention (count + days) — a daily `settings` run can no longer\n  evict your `full` archives.\n- `flock` concurrency lock, disk-space and cloud-reachability preflights,\n  documented exit-code map for cron agents.\n- `restore --target DIR` (staged), `--only GLOB` (selective), `--latest`;\n  v1 archives remain fully restorable.\n- New references: `credentials.md` (resolution chain + migration),\n  `setup-flow.md` (guided, gated first-time setup).\n\n### Deprecated (removed in v3)\n\n- `skills.entries.cloud-backup.env.ACCESS_KEY_ID` / `SECRET_ACCESS_KEY` /\n  `SESSION_TOKEN` / `GPG_PASSPHRASE` — still work, warn on every run.\n- Command `cleanup`, mode `skills`.\n\n## 1.1.5 — 2026-02\n\nLast v1 release. See git history.\n\nArchive v1.1.1: 15 files, 46196 bytes\n\nFiles: CHANGELOG.md (4746b), references/credentials.md (6277b), references/providers/aws-s3.md (2432b), references/providers/backblaze-b2.md (2187b), references/providers/cloudflare-r2.md (2301b), references/providers/digitalocean-spaces.md (2370b), references/providers/minio.md (2215b), references/providers/other.md (2938b), references/security.md (4792b), references/setup-flow.md (3330b), scripts/cloud-backup.sh (63156b), SECURITY.md (4512b), skill-card.md (3102b), SKILL.md (9174b), _meta.json (140b)\n\nFile v1.1.1:SKILL.md\n\n---\nname: cloud-backup\ndescription: Encrypt and upload OpenClaw state backups to S3-compatible storage (S3, R2, B2, MinIO). Use only when the user explicitly asks to back up or restore OpenClaw.\nmetadata: {\"openclaw\":{\"emoji\":\"☁️\",\"homepage\":\"https://github.com/obuchowski/openclaw-cloud-backup\",\"os\":[\"linux\",\"darwin\"],\"requires\":{\"bins\":[\"bash\",\"tar\",\"jq\"]},\"install\":[{\"kind\":\"brew\",\"formula\":\"awscli\",\"bins\":[\"aws\"]},{\"kind\":\"brew\",\"formula\":\"gnupg\",\"bins\":[\"gpg\"]}],\"primaryEnv\":\"CLOUD_BACKUP_GPG_PASSPHRASE\"}}\n---\n\n# OpenClaw Cloud Backup\n\nThe cloud layer for OpenClaw's native backup. Wraps `openclaw backup create`\n(config, credentials, consistent SQLite snapshots, workspace), then GPG-encrypts\nand uploads the archive to any S3-compatible bucket, with retention,\nverification, and staged restore.\n\nAll commands: `bash \"{baseDir}/scripts/cloud-backup.sh\" <subcommand>`\n\n## When to use this skill — and when not to\n\nAct ONLY on an explicit user request about OpenClaw backups: \"back up\nopenclaw\", \"restore my openclaw state\", \"set up cloud backups for openclaw\",\n\"/cloud-backup\", and similar.\n\n- Generic requests (\"back up my project\", \"save this file\", \"restore the\n  database\") are NOT for this skill. Ask what the user means before touching it.\n- NEVER run this skill as a side effect of another task, proactively, or\n  \"while you're at it\".\n- Read-only subcommands (`status`, `list`, `verify`, `schedule`, and any\n  `--dry-run`) may run freely once the user has asked about backups.\n  Everything else follows the gates below.\n\n## Confirmation gates\n\nBefore ANY state-changing action, show the user exactly what will happen and\nget an explicit yes. One gate per action — do not re-ask for things the user\njust confirmed, and never batch-confirm.\n\n| Action | What you MUST show before doing it |\n|---|---|\n| Write config (`openclaw config patch`) | Every key=value you will write, verbatim. Never write secrets — see Credentials. |\n| First backup to a new destination | Output of `backup <mode> --dry-run`: scope, sensitivity verdict, encryption status, target `s3://bucket/prefix`. |\n| Store/replace a credential | Only the file path + storage method (AWS profile / passphrase file). The secret value itself should not transit this conversation — the user runs those commands themselves. |\n| Restore | Step 1: always `restore <name> --dry-run` and show the file list. Step 2: state which paths will be overwritten (staged restores write to a fresh directory; `--in-place` overwrites live state), then require an explicit yes. Never skip the dry run. |\n| Prune | Output of `prune --dry-run`: which archives (local and remote) will be deleted, by name. |\n| Schedule creation | The exact `openclaw cron add ...` command and full payload text (see Scheduling). |\n\nRepeat manual backups to an already-confirmed destination need no new gate —\nthe user's request IS the confirmation. Still echo the one-line plan\n(\"full backup, encrypted, → s3://bucket/prefix\") before running.\n\n### Unattended runs (cron)\n\nA scheduled job's payload marks the run as operator-preconfirmed for `backup`\nand `prune` ONLY. In unattended runs: never restore, never change config,\nnever create or modify schedules, never store credentials.\n\n## Modes — what each backup contains\n\nEcho this table when the user asks what gets backed up:\n\n| Mode | Includes | Excludes by default | Sensitivity |\n|---|---|---|---|\n| `backup full` (default) | openclaw.json, credentials/, secret stores, state + agent SQLite snapshots, agent memory, workspace, installed skills | session transcripts, codex caches/logs, tools/, media/, logs/, old backups | SENSITIVE — encryption forced |\n| `backup full --everything` | everything above PLUS session transcripts and codex history | previous backup archives only | SENSITIVE — encryption forced |\n| `backup settings` | openclaw.json, secret stores, credentials/, auth files | everything else | SENSITIVE — always encrypted, no opt-out |\n| `backup workspace` | workspace directories (skills, memory files) | all state/config | encrypted by default; `--no-encrypt` allowed here only |\n\n\"SENSITIVE — encryption forced\" means the script refuses to produce a plaintext\narchive for that scope (exit 14). Do not work around it; if the user explicitly\nwants a plaintext-shareable archive, offer `config.excludeSecrets=true` instead.\nUsers can tune scope with `config.exclude` / `config.include` (state-relative\nglobs).\n\n## Subcommands\n\n| Command | What it does | Gate? |\n|---|---|---|\n| `backup [full\\|settings\\|workspace] [--everything] [--no-upload] [--dry-run] [--json]` | Create archive, encrypt, upload, apply retention | dry-run free; see gates |\n| `list` | Local + remote backups; flags failure debris | no |\n| `status` | Health: last backup, credential sources, sensitivity verdict, schedule, reachability | no |\n| `verify [name\\|--latest] [--deep]` | Checksum + decrypt + listing; `--deep` adds `openclaw backup verify` | no |\n| `restore <name\\|--latest> [--target DIR \\| --in-place] [--only GLOB] [--dry-run] [--yes] [--force]` | Staged restore by default | YES — two-step |\n| `prune [--dry-run]` | Apply retention; remove failure debris | YES |\n| `schedule` | PRINT the opt-in cron command (creates nothing) | no |\n| `setup` | Setup checklist + connection test (never writes config) | config writes gated individually |\n\nExit codes (report them precisely, especially from cron): 0 ok ·\n3 ok-with-warnings · 4 usage · 10 another run holds the lock · 11 missing\ndependency · 12 insufficient disk · 13 cloud unreachable/bad credentials ·\n14 encryption required but unavailable · 20-25 create/filter/encrypt/upload/\nverify failures · 30 restore failure.\n\n## First-time setup\n\nFollow `references/setup-flow.md` step by step. Summary: choose provider →\nuser creates a least-privilege bucket-scoped key (per provider guide) → store\ncredentials OUTSIDE OpenClaw config (AWS profile recommended) → write\nnon-secret config (gate) → test connection → enable encryption with a\ngenerated passphrase file → first manual backup (gate) → only then offer\nscheduling (gate).\n\n## Credentials\n\nResolution order and storage rules: `references/credentials.md`. Hard rules:\n\n- NEVER write access keys or passphrases into openclaw.json (no\n  `skills.entries.cloud-backup.env.*`). Backups archive that file: a plaintext\n  credential in config is carried inside every archive it protects.\n- S3 credentials live in an AWS named profile (`config.profile`, recommended)\n  or operator-managed process env. The passphrase lives in a chmod-600\n  passphrase file (`config.passphraseFile`).\n- Operators who run OpenClaw's secret store can point the skill at it\n  instead: `config.accessKeyRef` / `config.secretKeyRef` /\n  `config.passphraseRef` accept OpenClaw SecretRefs ({source: env|file|exec})\n  resolved against `.secrets.providers` — including 1Password-style exec\n  providers. Configured-but-broken refs abort the run; they never fall back.\n- Prefer flows where the secret never appears in this conversation: the user\n  runs `aws configure --profile ...` and the passphrase generator themselves.\n- If the script prints a DEPRECATED warning about plaintext config\n  credentials, surface it to the user verbatim and offer the migration in\n  `references/credentials.md`.\n\n## Scheduling — strictly opt-in\n\nNEVER create, modify, or enable a cron job by default, implicitly, or because\n\"backups should be scheduled\". Skills document cron setup; only the user\nopts in.\n\nOffer scheduling exactly once, AFTER the first successful manual backup:\n\n> \"Backup verified. Want me to schedule this daily? I'd create this cron job —\n> nothing is scheduled until you confirm:\"\n\nThen run `schedule` to print the exact command, adjust time/timezone/delivery\nto the user's setup, show it in full, and wait for an explicit yes before\nrunning it. If the user declines: drop it, and do not re-offer on later runs\n(check `openclaw cron list` first — if a cloud-backup job exists, never offer).\nTo remove a schedule: `openclaw cron rm <id>`.\n\n## Error handling (for you, the agent)\n\n- Surface script stderr to the user verbatim (trim to the relevant lines).\n  Never summarize an error into vagueness, never hide a WARN.\n- Non-zero exit: diagnose using the exit-code table, explain, propose ONE fix.\n  Never auto-retry `restore`, `prune`, or any `openclaw cron` mutation.\n  `backup` may be retried once, only after the cause is fixed and the user\n  agrees.\n- Never invent bucket names, endpoints, or passphrases. Missing value → ask.\n- Exit 14 (encryption required, no passphrase) fails hard by design — do not\n  work around it with `--no-encrypt` or an ad-hoc passphrase. Run setup.\n- The script cleans its staging on failure; if it reports it could not, tell\n  the user the exact leftover path.\n\n## Reference docs (read only when needed)\n\n- `references/credentials.md` — resolution chain, v1→v2 migration, warnings\n- `references/setup-flow.md` — guided first-time setup\n- `references/providers/{aws-s3,cloudflare-r2,backblaze-b2,digitalocean-spaces,minio,other}.md`\n- `references/security.md` — threat model, restore safety, incident response\n\nFile v1.1.1:_meta.json\n\n{\n  \"ownerId\": \"kn73d89b2a038m7cxgqww1w5ah81a7ar\",\n  \"slug\": \"openclaw-cloud-backup\",\n  \"version\": \"1.1.1\",\n  \"publishedAt\": 1781216330326\n}\n\nFile v1.1.1:references/credentials.md\n\n# Credentials — where each secret lives, and why\n\nThis skill needs up to three secrets: an S3 access key id, an S3 secret key,\nand a GPG passphrase. **None of them belong in openclaw.json.** Backups\narchive that file — a credential stored there rides along inside every\narchive it protects.\n\nThe recommended setup is two chmod-600 files:\n\n| Secret | Home | How |\n|---|---|---|\n| S3 key pair | AWS named profile (`~/.aws/credentials`) | `aws configure --profile openclaw-backup && chmod 600 ~/.aws/credentials`, then set `config.profile` |\n| GPG passphrase | passphrase file | `umask 077 && openssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase`, then set `config.passphraseFile` |\n\n## Resolution order (what the script actually does)\n\n**S3 credentials** (highest first):\n\n1. Process env `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` /\n   `AWS_SESSION_TOKEN` — operator-injected (systemd `EnvironmentFile=`,\n   gateway environment). Never overridden.\n2. **OpenClaw secret refs** — `config.accessKeyRef` + `config.secretKeyRef`\n   (+ optional `config.sessionTokenRef`). See \"Using the OpenClaw secret\n   store\" below. A configured-but-unresolvable ref aborts the run (exit 13) —\n   never a silent fallthrough to a weaker tier.\n3. `config.profile` → named profile in `~/.aws/credentials` (mode 600).\n   **Recommended simple default.** The skill passes `--profile`; the aws CLI\n   reads its own standard store. The secret never touches OpenClaw config or\n   this chat — and `~/.aws` is OUTSIDE the backup scope, so the key never\n   travels inside the archives it protects.\n4. DEPRECATED: `skills.entries.cloud-backup.env.ACCESS_KEY_ID` /\n   `.SECRET_ACCESS_KEY` / `.SESSION_TOKEN` plaintext in openclaw.json.\n   Still works in v2; warns loudly on every run; removed in v3.\n\n**GPG passphrase** (highest first):\n\n1. Process env `GPG_PASSPHRASE` — operator-injected.\n2. Process env `CLOUD_BACKUP_GPG_PASSPHRASE` — injected by OpenClaw from\n   `skills.entries.cloud-backup.apiKey` (which may itself be\n\nArchive v1.1.0: 15 files, 46137 bytes\n\nFiles: CHANGELOG.md (4746b), references/credentials.md (6277b), references/providers/aws-s3.md (2432b), references/providers/backblaze-b2.md (2187b), references/providers/cloudflare-r2.md (2301b), references/providers/digitalocean-spaces.md (2370b), references/providers/minio.md (2215b), references/providers/other.md (2938b), references/security.md (4792b), references/setup-flow.md (3330b), scripts/cloud-backup.sh (63156b), SECURITY.md (4512b), skill-card.md (3066b), SKILL.md (9174b), _meta.json (140b)\n\nArchive v1.0.0: 7 files, 15293 bytes\n\nFiles: README.md (4368b), references/local-config.md (1600b), references/provider-setup.md (3756b), references/security-troubleshooting.md (2908b), scripts/openclaw-cloud-backup.sh (23675b), SKILL.md (4886b), _meta.json (140b)","readmeExcerpt":"Skill: Cloud Backup [S3, R2, B2, MinIO & more] Owner: obuchowski Summary: Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw. Tags: latest:1.1.4 Version history: v1.1.4 | 2026-07-02T13:45:02.362Z | user 2.0.2 — 2026-07-02 Changed - Lean filter now excludes agents/*/qmd/xdg-cache/** (qmd embedding caches: downl","codeSnippets":[],"executableExamples":[{"language":"json","snippet":"{ \"skills\": { \"entries\": { \"cloud-backup\": { \"config\": {\n  \"accessKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/accessKeyId\" },\n  \"secretKeyRef\":  { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/secretAccessKey\" },\n  \"passphraseRef\": { \"source\": \"file\", \"provider\": \"default\", \"id\": \"/cloudBackup/gpgPassphrase\" }\n} } } } }"},{"language":"bash","snippet":"umask 077\nmkdir -p ~/.openclaw/credentials\nopenssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase\nopenclaw config patch 'skills.entries.cloud-backup.config.passphraseFile=\"~/.openclaw/credentials/cloud-backup.passphrase\"'"},{"language":"bash","snippet":"# 1. S3 keys → profile (run yourself; do not paste keys into agent chat)\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n\n# 2. Passphrase → file (see above). If you keep the SAME passphrase, just move\n#    it; if you pick a new one, save BOTH in your password manager.\n\n# 3. Remove the plaintext block, then verify\nopenclaw config patch 'skills.entries.cloud-backup.env=null'\njq '.skills.entries[\"cloud-backup\"]' ~/.openclaw/openclaw.json   # no \"env\" block\nbash scripts/cloud-backup.sh status                              # no DEPRECATED warnings"},{"language":"json","snippet":"{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Sid\": \"ListBucket\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Sid\": \"ObjectAccess\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}"},{"language":"bash","snippet":"aws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials"},{"language":"bash","snippet":"openclaw config patch 'skills.entries.cloud-backup.config.bucket=\"YOUR_BUCKET\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: cloud-backup\ndescription: Secrets-safe encrypted OpenClaw backups to S3/R2/B2/MinIO — lean modes, opt-in cron, staged restore. Use only when explicitly asked to back up/restore OpenClaw.\nmetadata: {\"openclaw\":{\"emoji\":\"☁️\",\"homepage\":\"https://github.com/obuchowski/openclaw-cloud-backup\",\"os\":[\"linux\",\"darwin\"],\"requires\":{\"bins\":[\"bash\",\"tar\",\"jq\"]},\"install\":[{\"kind\":\"brew\",\"formula\":\"awscli\",\"bins\":[\"aws\"]},{\"kind\":\"brew\",\"formula\":\"gnupg\",\"bins\":[\"gpg\"]}],\"primaryEnv\":\"CLOUD_BACKUP_GPG_PASSPHRASE\"}}\n---\n\n# OpenClaw Cloud Backup\n\nThe cloud layer for OpenClaw's native backup. Wraps `openclaw backup create`\n(config, credentials, consistent SQLite snapshots, workspace), then GPG-encrypts\nand uploads the archive to any S3-compatible bucket, with retention,\nverification, and staged restore.\n\nAll commands: `bash \"{baseDir}/scripts/cloud-backup.sh\" <subcommand>`\n\n## When to use this skill — and when not to\n\nAct ONLY on an explicit user request about OpenClaw backups: \"back up\nopenclaw\", \"restore my openclaw state\", \"set up cloud backups for openclaw\",\n\"/cloud-backup\", and similar.\n\n- Generic requests (\"back up my project\", \"save this file\", \"restore the\n  database\") are NOT for this skill. Ask what the user means before touching it.\n- NEVER run this skill as a side effect of another task, proactively, or\n  \"while you're at it\".\n- Read-only subcommands (`status`, `list`, `verify`, `schedule`, and any\n  `--dry-run`) may run freely once the user has asked about backups.\n  Everything else follows the gates below.\n\n## Confirmation gates\n\nBefore ANY state-changing action, show the user exactly what will happen and\nget an explicit yes. One gate per action — do not re-ask for things the user\njust confirmed, and never batch-confirm.\n\n| Action | What you MUST show before doing it |\n|---|---|\n| Write config (`openclaw config patch`) | Every key=value you will write, verbatim. Never write secrets — see Credentials. |\n| First backup to a new destination | Output of `backup <mode> --dry-run`: scope, sensitivity verdict, encryption status, target `s3://bucket/prefix`. |\n| Store/replace a credential | Only the file path + storage method (AWS profile / passphrase file). The secret value itself should not transit this conversation — the user runs those commands themselves. |\n| Restore | Step 1: always `restore <name> --dry-run` and show the file list. Step 2: state which paths will be overwritten (staged restores write to a fresh directory; `--in-place` overwrites live state), then require an explicit yes. Never skip the dry run. |\n| Prune | Output of `prune --dry-run`: which archives (local and remote) will be deleted, by name. |\n| Schedule creation | The exact `openclaw cron add ...` command and full payload text (see Scheduling). |\n\nRepeat manual backups to an already-confirmed destination need no new gate —\nthe user's request IS the confirmation. Still echo the one-line plan\n(\"full backup, encrypted, → s3://bucket/prefix\") before running.\n\n### Unattended "},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn73d89b2a038m7cxgqww1w5ah81a7ar\",\n  \"slug\": \"openclaw-cloud-backup\",\n  \"version\": \"1.1.4\",\n  \"publishedAt\": 1782999902362\n}"},{"path":"references/credentials.md","content":"# Credentials — where each secret lives, and why\n\nThis skill needs up to three secrets: an S3 access key id, an S3 secret key,\nand a GPG passphrase. **None of them belong in openclaw.json.** Backups\narchive that file — a credential stored there rides along inside every\narchive it protects.\n\nThe recommended setup is two chmod-600 files:\n\n| Secret | Home | How |\n|---|---|---|\n| S3 key pair | AWS named profile (`~/.aws/credentials`) | `aws configure --profile openclaw-backup && chmod 600 ~/.aws/credentials`, then set `config.profile` |\n| GPG passphrase | passphrase file | `umask 077 && openssl rand -base64 32 > ~/.openclaw/credentials/cloud-backup.passphrase`, then set `config.passphraseFile` |\n\n## Resolution order (what the script actually does)\n\n**S3 credentials** (highest first):\n\n1. Process env `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` /\n   `AWS_SESSION_TOKEN` — operator-injected (systemd `EnvironmentFile=`,\n   gateway environment). Never overridden.\n2. **OpenClaw secret refs** — `config.accessKeyRef` + `config.secretKeyRef`\n   (+ optional `config.sessionTokenRef`). See \"Using the OpenClaw secret\n   store\" below. A configured-but-unresolvable ref aborts the run (exit 13) —\n   never a silent fallthrough to a weaker tier.\n3. `config.profile` → named profile in `~/.aws/credentials` (mode 600).\n   **Recommended simple default.** The skill passes `--profile`; the aws CLI\n   reads its own standard store. The secret never touches OpenClaw config or\n   this chat — and `~/.aws` is OUTSIDE the backup scope, so the key never\n   travels inside the archives it protects.\n4. DEPRECATED: `skills.entries.cloud-backup.env.ACCESS_KEY_ID` /\n   `.SECRET_ACCESS_KEY` / `.SESSION_TOKEN` plaintext in openclaw.json.\n   Still works in v2; warns loudly on every run; removed in v3.\n\n**GPG passphrase** (highest first):\n\n1. Process env `GPG_PASSPHRASE` — operator-injected.\n2. Process env `CLOUD_BACKUP_GPG_PASSPHRASE` — injected by OpenClaw from\n   `skills.entries.cloud-backup.apiKey` (which may itself be a SecretRef).\n   Resolves only inside the OpenClaw agent runtime, so a bare-shell\n   `cloud-backup.sh backup` won't see it — prefer `passphraseRef` below.\n3. **OpenClaw secret ref** — `config.passphraseRef` (below). Works from any\n   shell; aborts with exit 14 if configured but unresolvable.\n4. `config.passphraseFile` — path to a mode-600 file. **Recommended simple\n   default.** The script refuses world-readable files and warns on group\n   access. The passphrase is passed to gpg over a file descriptor — never on\n   a command line (v1 leaked it into `ps`/`/proc/*/cmdline`).\n5. DEPRECATED: `skills.entries.cloud-backup.env.GPG_PASSPHRASE` plaintext in\n   openclaw.json. Warns on every run; removed in v3.\n\n`status` always prints where each secret resolved from — check it whenever\nyou are unsure which tier is active.\n\n## Using the OpenClaw secret store (config.*Ref)\n\nIf you already run OpenClaw's secret system (`openclaw secrets configure`,\n`.secrets.providers` in openclaw.json"},{"path":"references/providers/aws-s3.md","content":"# AWS S3\n\n## 1. Create a private bucket\n\n1. AWS Console → S3 → **Create bucket**\n2. Keep **Block Public Access** enabled (all four checkboxes)\n3. Enable **Bucket Versioning** (recommended — protects against overwrites)\n4. Use **SSE-S3** encryption (default, free)\n\n## 2. Create a least-privilege key\n\nCreate a dedicated IAM user with programmatic access. Never use root keys.\nAttach this policy (replace `YOUR_BUCKET`):\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    { \"Sid\": \"ListBucket\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:ListBucket\"], \"Resource\": \"arn:aws:s3:::YOUR_BUCKET\" },\n    { \"Sid\": \"ObjectAccess\", \"Effect\": \"Allow\",\n      \"Action\": [\"s3:GetObject\", \"s3:PutObject\", \"s3:DeleteObject\"],\n      \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/*\" }\n  ]\n}\n```\n\nIAM → Users → Create user → attach policy → Create access key.\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup\nchmod 600 ~/.aws/credentials\n```\n\nOn EC2/ECS, prefer an instance/task role and skip stored keys entirely.\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"YOUR_BUCKET\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-east-1\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\n**No `endpoint`** — AWS S3 is the one provider where it stays unset.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Common regions: `us-east-1`, `us-west-2`, `eu-west-1`, `eu-central-1`.\n- If using S3 Object Lock or Glacier, extend the IAM policy accordingly.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`."},{"path":"references/providers/backblaze-b2.md","content":"# Backblaze B2 (S3-compatible API)\n\n## 1. Create a private bucket\n\n1. Backblaze Console → **B2 Cloud Storage** → **Create a Bucket**\n2. Set to **Private**\n3. Disable Object Lock unless you need immutable backups\n\n## 2. Create a least-privilege key\n\n1. **App Keys** → **Add a New Application Key**\n2. **Restrict to your backup bucket**\n3. Allow: `listBuckets`, `listFiles`, `readFiles`, `writeFiles`, `deleteFiles`\n4. Note the **keyID** (= access key) and **applicationKey** (= secret key)\n\n## 3. Store the credentials (run this yourself — keep keys out of the chat)\n\n```bash\naws configure --profile openclaw-backup   # paste keyID + applicationKey\nchmod 600 ~/.aws/credentials\n```\n\n## 4. Configure the skill (non-secret keys only)\n\n```bash\nopenclaw config patch 'skills.entries.cloud-backup.config.bucket=\"my-backup-bucket\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.region=\"us-west-004\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.endpoint=\"https://s3.us-west-004.backblazeb2.com\"'\nopenclaw config patch 'skills.entries.cloud-backup.config.profile=\"openclaw-backup\"'\n```\n\nThe region is on the bucket details page and must match the endpoint.\n\n## Credential safety (read me)\n\n- This key can read, write, and DELETE your backups. Treat it like a password.\n- Never commit it to git. Never store it in openclaw.json: **backups archive\n  openclaw.json itself** — a key stored there rides along inside every archive\n  it protects.\n- Scope the key to this one bucket where the provider supports it. An\n  account-wide key turns one leaked archive into an account takeover.\n- Rotate every ~90 days and immediately on any suspicion: create new key →\n  update the profile → verify a backup → revoke the old key.\n\n## Notes\n\n- Free tier: 10 GB storage, 1 GB/day egress.\n- Bucket-scoped application keys cannot list other buckets — that is the point.\n- Rotating the master key revokes ALL application keys.\n\n## Deprecated (v1): keys in OpenClaw config — do not use\n\nv1 documented `skills.entries.cloud-backup.env.ACCESS_KEY_ID/SECRET_ACCESS_KEY`.\nIt still works in v2 with loud warnings and is removed in v3. Migration:\n`references/credentials.md`."}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":null,"editorialQuality":{"score":100,"threshold":65,"status":"thin","wordCount":2621,"uniquenessScore":37,"reasons":["uniqueness-below-45"]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-11T18:42:40.919Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-11T21:50:04.817Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}