Skip to content

Migration assistant: find and import local Codex accounts on demand #77

Closed

Description

@squarepots

Problem

GSwitch can import an explicitly selected Cockpit Tools export, but migration still requires the user to:

  1. open another account manager;
  2. export accounts;
  3. return to GSwitch;
  4. choose the export file.

For a local account switcher, importing account credentials that already exist on the same computer should be easier.

This is a portability feature, not continuous synchronization.

Product decision

Add an explicit Import from this computer flow to the Add Account dialog.

The user must trigger it.

Only after that click may GSwitch probe a small allowlist of known account locations for supported tools.

There is:

  • no startup scan;
  • no periodic scan;
  • no file watcher;
  • no daemon;
  • no hidden synchronization relationship.

A scan is a one-shot migration operation.

Initial sources

Support only sources with a concrete current contract.

Official Codex

Inspect the known current local Codex credential/profile location already understood by GSwitch.

If the current official Codex identity is already saved, report it as already present rather than replacing anything.

Cockpit Tools

Support the current Cockpit Codex account store as an explicit migration adapter.

Current Cockpit storage uses:

  • account index: ~/.antigravity_cockpit/codex_accounts.json;
  • account details: ~/.antigravity_cockpit/codex_accounts/*.json;
  • encrypted detail key when applicable: ~/.antigravity_cockpit/secure-account-storage.key.

Also recognize the documented legacy app-data location when it is unambiguous.

Respect Cockpit's documented data-root override only when the source root is explicitly selected/provided to GSwitch; do not search the environment or disk recursively trying to guess custom installations.

Provide Choose another folder… as a fallback for a custom Cockpit data root.

Cockpit encrypted storage

Current Cockpit can store per-account details in an AES-256-GCM envelope while leaving the index plaintext.

GSwitch should support this current format because otherwise automatic migration fails for current installs.

Implement a narrowly scoped read-only decoder for the known Cockpit envelope:

  • version 1 only;
  • AES-256-GCM only;
  • existing secure-account-storage.key only;
  • 32-byte decoded key;
  • 12-byte nonce;
  • decode/decrypt in Rust only.

Hard rules:

  • never create a missing Cockpit key;
  • never rotate a Cockpit key;
  • never rewrite a Cockpit account file;
  • never repair/migrate Cockpit storage;
  • never expose decrypted source records to React;
  • unknown envelope version/algorithm is unsupported, not guessed.

This code belongs in the migration adapter, not in GSwitch's own credential storage.

If Cockpit changes its storage contract, fail closed and offer the existing export-file import path.

Discovery result

Scanning must return a sanitized preview, not credentials.

Each discovered candidate should expose only presentation/status fields such as:

  • email;
  • workspace/account name;
  • plan when already known locally;
  • source (Official Codex, Cockpit Tools);
  • state: New, Already in GSwitch, or Unsupported.

No access token, refresh token, raw JSON, encryption key, full source record, or provider response crosses into React.

Default selection: all New candidates.

Already-present identities are disabled/no-op by default.

Import

After the user confirms:

  1. reread the selected source records;
  2. rederive identity so the preview cannot become stale silently;
  3. convert only the minimum supported source shape into the complete Codex credential document expected by GSwitch;
  4. reuse the normal intake path from Account intake: prefer read-only token snapshots before managed refresh #74;
  5. deduplicate by stable AccountIdentity;
  6. save into GSwitch without changing the source application.

Never silently replace an existing GSwitch credential merely because another application's copy is newer or different.

Credential replacement/synchronization is not part of migration.

Source adapters, not a plugin platform

Use a small fixed Rust enum/adapter boundary for the supported local sources.

The first implementation owns only Official Codex and Cockpit Tools.

Do not build:

  • a plugin SDK;
  • arbitrary filesystem search;
  • a registry downloaded from the internet;
  • app-specific background agents;
  • a general credential synchronization engine.

A future app is added only with a concrete known storage contract and focused tests.

UI

In Add Codex account, keep the existing simple actions and add one migration-oriented row:

Import from this computer
Find Codex accounts already stored by supported apps.

Opening it shows the one-shot discovery preview.

Keep Import account files as the universal manual fallback (#76).

Do not put Cockpit-specific explanation on the main account screen.

Security / privacy copy

The scan UI must say what GSwitch is about to inspect before it does so.

Update durable docs to replace the current absolute rule that GSwitch “never reads Cockpit private storage” with the narrower rule:

GSwitch does not inspect another application's account storage automatically. It may read a documented supported local source only after the user explicitly starts an import-from-this-computer operation, and it never modifies that source.

Update:

  • docs/workflows.md
  • docs/security.md
  • docs/architecture.md
  • add-account UI copy in both languages.

Tests

Use temporary directories and fixture records only.

Cover at least:

  • no discovery happens during app startup;
  • clicking discovery probes only allowlisted paths;
  • official Codex candidate discovery;
  • Cockpit plaintext legacy/current candidate;
  • Cockpit v1 AES-256-GCM candidate;
  • missing Cockpit key fails without creating one;
  • bad key / bad nonce / unknown version / unknown algorithm fail closed;
  • no source file is changed by scan or import;
  • preview contains no credentials or encryption key;
  • existing GSwitch identity is shown as already present;
  • preview-to-confirm revalidation catches changed source identity;
  • custom selected source root is bounded to that selected folder;
  • mixed new/duplicate/unsupported candidates;
  • imported accounts use the normal intake path;
  • no startup watcher/scheduler is introduced.

Dependencies

Build on:

The migration adapter may add the minimal cryptography dependency required to read Cockpit's published current AES-GCM envelope. Do not import Cockpit as a library or vendor its account manager.

Execution and closure

Implement, test, review/fix, merge, and close this issue when the automated acceptance criteria pass.

Do not require the maintainer to install Cockpit on every supported OS solely to close the issue; fixture coverage and CI are the acceptance evidence.

Done

A user can explicitly ask GSwitch to find supported Codex accounts already stored on the computer, preview them, and import selected new accounts without exporting files first, while GSwitch remains a one-shot migration tool rather than a background synchronizer.

Exact current Cockpit storage contract

The adapter is intentionally pinned to the current open-source Cockpit storage contract rather than to a guessed filesystem shape.

Current production data root:

~/.antigravity_cockpit

Current Codex files:

codex_accounts.json
codex_accounts/<account-id>.json
secure-account-storage.key

Cockpit also has a development profile (.antigravity_cockpit_dev) and a COCKPIT_TOOLS_DATA_DIR override. Do not probe development/custom roots automatically. The normal production root is the only default Cockpit probe; Choose another folder… handles an explicit custom root.

The current encrypted detail envelope contains:

  • version;
  • kind;
  • algorithm;
  • key_id;
  • nonce;
  • ciphertext;
  • encrypted_at.

For automatic Cockpit migration, accept only the currently understood Codex envelope:

  • version 1;
  • kind codex;
  • algorithm AES-256-GCM;
  • current recognized local key-id contract;
  • existing base64-encoded 32-byte key;
  • base64-encoded 12-byte nonce.

Do not call or reproduce Cockpit's normal deserialize path when it can rotate/rewrite old data. GSwitch needs its own narrowly scoped read-only decoder: open existing files, decrypt/parse, and close them without any migration, repair, rotation, backup, or write-back behavior.

Plaintext legacy Codex account details may be read when they match the known Cockpit Codex schema. Unknown wrappers or schemas are unsupported and fall back to manual file export/import.

Dependency PR boundary for v1.0.2

Do not merge, close, rebase, rewrite, or otherwise fold the currently open Dependabot PRs into this issue's implementation work.

Current unrelated dependency PRs:

They own the six non-main branches currently visible in the repository.

Leave them open during #73-#77 so this product cycle stays reviewable and does not accumulate unrelated dependency churn.

After #73-#77 are merged and v1.0.2 is published, dependency maintenance is handled separately in #78.

Do not delete an open Dependabot branch merely to reduce the branch count. The repository already has automatic head-branch deletion enabled after merge.

Activity

  1. squarepots commented on Sep 21, 2026

    @squarepots
    MemberAuthor

    Implemented and merged in PR #82 (merge commit 33bba7f). Acceptance is covered by local Rust/UI tests, locked CI across frontend/Linux/macOS/Windows, and CodeQL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions