Files
wehub-resource-sync 426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

7.4 KiB

Agent backup — real state surface

Status: implemented contract. The agent server now exposes /api/snapshot and /api/restore backed by a full-agent manifest covering database, media, vault ciphertext, character config, and remaining state-dir files. Cloud backup rows store that manifest as a KMS-encrypted full backup (R2/offloaded when heavy-payload storage is configured), and pre-upgrade snapshots are mandatory before fleet swaps so rollback can replay the restore point. Issues #9963 and #9964.

Why this exists

The legacy snapshot payload was a 3-field toy:

interface AgentBackupStateData {
  memories: Array<{ role: string; text: string; timestamp: number }>;
  config: Record<string, unknown>;
  workspaceFiles: Record<string, string>;
}

AgentBackupStateData remains backward-compatible with those fields, but a real backup now carries manifest?: AgentBackupManifest (src/db/schemas/agent-sandboxes.ts). The deployed agent server produces the manifest in packages/agent/src/services/agent-backup.ts and serves it from packages/agent/src/api/server.ts. Cloud still recognizes SNAPSHOT_ENDPOINT_UNSUPPORTED for old images during scheduled auto snapshots, but pre-upgrade snapshots must contain a manifest or the upgrade is refused.

The real state surface a backup MUST cover

A faithful backup is a manifest of components, each with its own integrity hash, so a partial/corrupt restore is detectable and fails loudly rather than booting a half-restored agent.

Component Captured by Notes
Database PGlite filesystem snapshot (PGLITE_DATA_DIR, default .eliza/.elizadb) or external Postgres logical table rows selected by agent ownership. Restore replaces the PGlite dir after closing the adapter, or deletes/reinserts agent-owned Postgres rows in a transaction.
Content-addressed media ${STATE_DIR}/media/<sha256>.<ext> Restore verifies every file byte hash and replaces the media root, avoiding stale attachments.
Vault / secrets vault.json, .vault-pglite/**, audit/vault.jsonl Backed up as stored bytes/ciphertext only. Restore prunes stale vault files and never decrypts secrets.
Character + remaining state-dir Runtime character, config file, and non-log state-dir files excluding media/backups/vault/database-owned dirs. Restore verifies hashes, rewrites the config file, prunes stale scoped state files, and returns requiresRestart: true.

Per-component integrity hashes

The manifest stores a sha256 per component and per file. Restore recomputes the file-set, Postgres dump, and manifest component hashes before applying; a mismatch aborts restore. Cloud backup rows still store content_hash, and agent-backup-diff now forces manifest-bearing snapshots to remain full backups so the legacy incremental delta format cannot drop component blobs.

Storage target — dual: local file + cloud R2

The backup lands through the existing backup row and heavy-payload storage path:

  1. Agent/local runtime/api/snapshot returns the full manifest payload; /api/restore applies it to the agent's configured state/database locations.
  2. Cloud backup rowsprepareAgentBackupInsertData encrypts agent_sandbox_backups.state_data with the existing org-scoped KMS field crypto before storage. Large encrypted payloads then flow through offloadJsonField to R2/S3-compatible object storage when configured, leaving an inline empty preview and a state_data_key. Repository reads decrypt at the hydration boundary, so restore callers never handle ciphertext directly.

Relationship to existing primitives (reuse, do NOT duplicate)

  • Backup rows: reuse the agent_sandbox_backups table and the agent-backup-diff full/incremental delta engine (src/lib/services/agent-backup-diff.ts). Do NOT add a second backup table or a parallel snapshot store.
  • Snapshot types: the real manifest still flows through snapshot_type (auto | manual | pre-shutdown | pre-upgrade). The pre-upgrade type is the restore point executeDowngrade replays on rollback (#9964).
  • Restore: reuse getReconstructedBackupState() for chain replay and the bridge /api/restore push.
  • Rollback: executeUpgrade refuses to swap without a manifest-bearing pre-upgrade backup. executeDowngrade provisions blue on previous_image_digest, pushes the reconstructed pre-upgrade state before cutover, and fails loudly if the restore point is missing or rejected. The operator route enqueues agent_downgrade daemon jobs; it never runs automatically.

Operational proof

The code path is tested for local manifest backup/restore, corrupt backup refusal, encrypted local file restore, cloud diff safety, encrypted backup row storage, metadata-only backup listing, pre-upgrade blocking, rollback restore, and daemon rollback job execution.

Full PR evidence still requires a live staging run: backup -> wipe -> restore, plus upgrade -> rollback, with real agent logs, DB/media artifacts, screenshots/video, and a live LLM trajectory per AGENTS.md.

Image upgrade ↔ rollback & DB-migration discipline (#9964)

Dedicated agents share one Postgres per environment (prod/staging) — there is no per-agent DB branch. A fleet image upgrade is therefore a shared-schema change: the new image's plugin-sql migrations run at container boot against the DB that agents still on the old image are also using. executeDowngrade rolls the image back (onto previous_image_digest, restoring the pre-upgrade snapshot before cutover), but it cannot roll a destructive forward migration back — a dropped column / retyped column / dropped table is gone the moment the new image applied it, and the rolled-back old image then reads a schema it no longer matches.

Rule: agent-image migrations MUST be expand/contract (additive-only).

  • Expand (the upgrade): only add — new nullable columns, new tables, new indexes (ADD COLUMN IF NOT EXISTS, CREATE TABLE IF NOT EXISTS). The new image reads the old schema; the old image ignores the new objects. This keeps a mixed-version fleet (some agents up/down mid-rollout, capped at MAX_INFLIGHT_UPGRADES) correct, and keeps executeDowngrade a real restore point rather than a swap into a broken schema.
  • Contract (the cleanup): a column drop / rename / type change is a separate, later migration, shipped only after the whole fleet is on the new image and no rollback to the pre-expand image is wanted. Never combine expand + contract in the image that a rollback might return from.
  • Never put a destructive DDL in the same image version as the feature that needs it. If a value must change shape, expand (add the new column, backfill, dual-write), cut over reads in a later image, then contract.

This mirrors the repo-wide migration rule (CLAUDE.md: append-only, IF NOT EXISTS/IF EXISTS, small targeted migrations) and makes it binding for the agent-image upgrade path specifically, where a shared DB + a real rollback path raise the stakes. A migrate-verify-on-boot gate that health-fails an upgrade whose migrations did not apply cleanly is the next step, but is daemon/image work (see "Out of scope" above).