Skip to content

Export, import and local backups on the control plane and the CLI - #349

Merged
fylorn merged 2 commits into
feat/sync-backupfrom
feat/pack-backup-2
Oct 19, 2026
Merged

fylorn merged 2 commits into
feat/sync-backupfrom
feat/pack-backup-2

Conversation

@fylorn

@fylorn fylorn commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Builds on #348. Export, import, local backups that replace the change history, restore, Lite's client preferences and pinned keys, on the control plane and the command line. Control API version 50.

Control plane

  • GET /config/items: every item with what it refers to.
  • POST /config/export: all items, or chosen ones with what they refer to (scope is "all" or { "items": [...] }); with or without keys (${VAR} values are kept), optionally with a passphrase. No local settings and no ChatGPT login in the pack; omitted_logins names the upstreams that need a new sign-in.
  • POST /config/import/preview / POST /config/import: item by item new / same / conflict / error, with flags (missing_key, needs_login, plugin_disabled), the differing merge units, a suggested name, the error and the item's refs (so the app can mark what depends on a skipped item). Decisions are import / overwrite / rename / skip; a rename rewrites the references inside the pack. Imported plugins arrive disabled, upstreams without keys or without a login arrive disabled, blank gateway keys are regenerated. Errors: a reference to something neither here nor in the pack, a missing AWS profile, a plugin script that is not the approved one, a key value that another key here has, or an item that does not validate once imported.
  • GET /backups, POST /backups/file, POST /backups/preview, POST /backups/restore: the last 10 local versions, each a full pack (plugins and client preferences included, control key masked), merged by content id with the versions of the sync location once sync registers a RemoteBackups source. Restore can take a subset of items; it keeps local settings and current ChatGPT logins and does not delete pinned keys. In safe mode a version comes back whole with the current control key.
  • GET / PUT /client-prefs (opaque, 64 KiB at most) and PUT /pinned-keys.
  • Events: gateway_keys_changed { names } after an import overwrite or a restore changed or removed key values; client_prefs_changed.
  • Removed: GET /config/history, POST /config/rollback. ConfigOrigin drops rollback and gains sync, import, restore, repair (the one-click repair used to be recorded as ui).

Over a remote connection the client preferences live in the app: export uses the ones in the request, and import and restore leave the core's alone (the import preview hands the pack's preferences to the app).

Behaviour changes

  • Every write leaves a backup version before and after, as the history did; a version with the same content is not kept again, a credential rotation never makes one, and the old history/*.yaml files are removed. The core records the state on disk when it starts.
  • A disabled upstream may lack its credential (blank key, OAuth refresh token or AWS keys; a ChatGPT account without a sign-in): that is how imported ones arrive.
  • A plugin that can rewrite tool calls is never approved by an import, a restore or anything else that lands a snapshot: it waits for approval through the confirmed path, as installing it from the plugin page does.

CLI

twcore export [--providers a,b] [--no-secrets] [--encrypt] -o FILE, twcore import FILE [--dry-run] [--on-conflict overwrite|rename|skip], twcore backup list | restore ID [--dry-run]. They work on the files directly, like twcore config. config history and config rollback are gone; docs/config.md (and the Chinese page) describe the new commands.

Tests

Format, merge and import tables in tw-pack (cargo test -p tw-pack), endpoint tests in crates/tw-control/tests/backups.rs, and the existing history tests moved to backups. Local runs: cargo fmt --check, cargo clippy --workspace --all-targets -D warnings (stable and 1.99), cargo test --workspace, the TypeScript export through tsc --strict, and tw-pack plus the CLI's terminal code under clippy for x86_64-pc-windows-msvc.

🤖 Generated with Claude Code

fylorn and others added 2 commits October 19, 2026 09:10
- Endpoints: /config/items, /config/export, /config/import/preview,
  /config/import, /backups, /backups/file, /backups/preview,
  /backups/restore, /client-prefs, /pinned-keys. Import goes item by item
  (new, same, conflict, error; import, overwrite, rename, skip) and says
  what each row refers to; restore can take a subset of items, keeps the
  local settings, ChatGPT sign-ins and pinned keys, and in safe mode
  brings a version back whole with the current control key.
- Local backups replace the change history: every write leaves a full
  configuration pack (plugins and client preferences included, control key
  masked) before and after, the last 10 are kept, rotations make none,
  and the old history files go. /config/history and /config/rollback are
  removed; ConfigOrigin drops rollback and gains sync, import, restore,
  repair.
- New events gateway_keys_changed and client_prefs_changed. Over a remote
  connection the client preferences stay in the app.
- RemoteBackups lets sync add the versions of the sync location to the
  list; pinned keys are kept in the data directory.
- A disabled upstream may lack its credential, which is how imported ones
  arrive. A plugin that can rewrite tool calls is never approved by an
  import or a restore.
- CLI: twcore export / import / backup list / backup restore; config
  history and config rollback are gone.
- Control API version 50.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@fylorn
fylorn merged commit 13dc22a into feat/sync-backup Oct 19, 2026
7 checks passed
@fylorn
fylorn deleted the feat/pack-backup-2 branch October 19, 2026 01:58
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant