发布

  • feat(cli): migrate API key storage from plaintext to OS keyring (#3202)

    frostbyte_neo 发布于 2026-04-14 07:13:41 +00:00

    Summary

    • Wire @composio/cli-keyring into ComposioUserContext so the API key
      lives in the macOS Keychain / Linux Secret Service instead of plaintext
      ~/.composio/user_data.json
    • Read precedence: env var (COMPOSIO_USER_API_KEY) > OS keyring >
      legacy plaintext
    • One-shot migration: on first run after upgrade, if the keyring is
      empty but user_data.json has an api_key, copy it to the keyring and
      strip it from JSON
    • In-process memoization: keyring read happens once at layer build
      (~1.4ms), held in closure for the rest of the process
    • dangerouslySaveApiKeyInUserConfig: opt-in escape hatch in
      ~/.composio/config.json for headless Linux / containers / CI where the
      keyring is unavailable
    • ctx.data is now a getter: fixes a pre-existing bug where
      mutations via login()/update() were invisible through the stale
      snapshot
    • In-memory mock keyring for test isolation — tests never touch the
      real keychain

    Files changed

    file what
    ts/packages/cli/package.json Add @composio/cli-keyring workspace
    dep
    ts/packages/cli/src/models/cli-user-config.ts
    dangerously_save_api_key_in_user_config schema field
    ts/packages/cli/src/services/cli-user-config.ts Surface new field
    on CliUserConfigResolved
    ts/packages/cli/src/services/user-context.ts Keyring-first
    read/write/migrate/fallback chain
    ts/packages/cli/src/cli-main.ts Wire KeyringLive into layer
    composition
    ts/packages/cli-keyring/src/stores/macos-security-ffi.ts Fix:
    writes via subprocess -A, cleanup dead ACL builder code, add base64
    encoding
    ts/packages/cli/test/__utils__/services/test-layer.ts In-memory
    mock keyring for test isolation
    ts/packages/cli/test/src/commands/login.cmd.test.ts Update
    assertion: api_key no longer in JSON after login

    Verified end-to-end

    $ ./dist/composio version
    INFO: Composio API key migrated from user_data.json to the OS keyring
    0.2.22
    
    $ cat ~/.composio/user_data.json   # no api_key
    {"base_url":"...","web_url":"...","org_id":"...","test_user_id":"..."}
    
    $ # Rebuild binary (new hash) → read from keychain → no dialog
    $ ./dist/composio whoami
    {"email":"rahul@composio.dev",...}
    

    Test plan

    • 676 CLI tests passing (676 passed, 1 skipped)
    • Login test updated: asserts api_key absent from JSON + present
      via ctx.data.apiKey
    • In-memory mock keyring — no real keychain access in tests
    • Binary build + FFI roundtrip verified
    • Rebuild with new hash → no keychain dialog (allow-any ACL via
      -A)

    🤖 Generated with Claude Code

    Stack: 2/2 — base PR: cli/keyring-package (#3201)

    Co-authored-by: Claude Opus 4.6 (1M context) noreply@anthropic.com

    下载附件