A native macOS desktop app to switch between multiple GitHub accounts with a single click. Updates your macOS Keychain credentials, global git config (user.name/user.email), and GitHub CLI sessions simultaneously.
Built with Electrobun (TypeScript + Bun + native macOS webview).
- Instant Account Switching - Switch between GitHub accounts in one click with atomic, transactional updates
- Keychain Integration - Reads and writes macOS Keychain credentials via
git credential-osxkeychain - Git Config Management - Automatically updates
~/.gitconfigwith the correctuser.nameanduser.email - GitHub CLI Support - Detects and switches
ghCLI sessions (gh auth switch) - System Tray - Quick-access tray menu for switching without opening the main window
- Credential Discovery - Auto-detects existing GitHub credentials from your Keychain on first launch
- Encrypted Storage - Account data stored in AES-256-GCM encrypted JSON files
- Rollback on Failure - If any step of the switch fails, all changes are rolled back to the previous state
- System Notifications - Optional notifications when switching accounts
- Launch at Login - Optional LaunchAgent for auto-start
- Beautiful UI - GitHub-inspired dark theme with glass effects and spring animations
Coming soon
- macOS (required - uses native Keychain and system webview)
- Bun v1.0+ (install)
- Git (pre-installed on macOS)
- GitHub CLI (optional, for CLI session switching) -
brew install gh
bun install# Build and run the app
bun run start
# Run with Vite HMR (hot module replacement)
# Terminal 1: Start Vite dev server
bun run hmr
# Terminal 2: Build and launch Electrobun
bun run devbun run buildThe built .app bundle will be in build/.
| Layer | Technology |
|---|---|
| Framework | Electrobun v1 |
| Runtime | Bun |
| Frontend | React 18 + Tailwind CSS 3 + Vite |
| Animations | Framer Motion (spring physics) |
| Language | TypeScript |
| Storage | Encrypted JSON (AES-256-GCM) |
| IPC | Electrobun typed RPC |
git-account-switcher/
├── electrobun.config.ts # Electrobun build config
├── src/
│ ├── shared/ # Shared types between Bun and webview
│ │ ├── types.ts # Data models (GitAccount, AppSettings, etc.)
│ │ ├── rpc-types.ts # Typed RPC schema (bun <-> webview)
│ │ ├── validation.ts # Input validation (email, username, PAT)
│ │ └── constants.ts # App constants and config
│ ├── bun/ # Main process (Bun runtime)
│ │ ├── index.ts # Entry point: windows, tray, menus, RPC handlers
│ │ └── services/
│ │ ├── account-store.ts # Account CRUD + encrypted persistence
│ │ ├── keychain-service.ts # macOS Keychain read/write/delete
│ │ ├── git-config-service.ts # git config get/set operations
│ │ ├── github-cli-service.ts # gh CLI detection and switching
│ │ ├── notification-service.ts
│ │ ├── launch-at-login.ts # LaunchAgent plist management
│ │ └── process-runner.ts # Sandboxed Bun.spawn() wrapper
│ └── mainview/ # Webview (React)
│ ├── index.html
│ ├── index.css # Tailwind + glass effects
│ ├── main.tsx # Entry point
│ ├── App.tsx # Root component with view routing
│ ├── hooks/
│ │ ├── useAccounts.ts # Account state management via RPC
│ │ ├── useSettings.ts # Settings state via RPC
│ │ └── useRPC.ts # Electroview RPC initialization
│ └── components/
│ ├── MainWindow.tsx # Account list with header/footer
│ ├── AccountCard.tsx # Account card with switch/edit/delete
│ ├── EmptyState.tsx # No accounts placeholder
│ ├── AddEditAccountModal.tsx # Account form with validation
│ ├── TokenHelpModal.tsx # PAT creation instructions
│ ├── ConfirmDialog.tsx # Delete confirmation
│ ├── SettingsView.tsx # Tabbed settings (General/Accounts/About)
│ ├── WelcomeWizard.tsx # First-launch onboarding
│ ├── CLISetupWizard.tsx # GitHub CLI setup wizard
│ └── StepIndicator.tsx # Wizard progress dots
When you switch accounts, the app performs these steps atomically:
- Snapshot current state (Keychain credential + git config)
- Update Keychain - Erase old credential, store new one via
git credential-osxkeychain - Update git config - Set
user.nameanduser.emailglobally - Clear credential cache - Run
git credential-cache exit - Switch GitHub CLI - Run
gh auth switch --user <username>(if installed) - Notify - Show system notification (if enabled)
If any step fails, all previous steps are rolled back to the snapshot.
- Passwords/tokens are stored in the macOS Keychain (not in plain text)
- Account metadata is encrypted with AES-256-GCM
- All subprocess calls use a sanitized environment (restricted
PATH,GIT_TERMINAL_PROMPT=0,GIT_CONFIG_NOSYSTEM=1) - PAT tokens are validated against GitHub's format before saving
- Token input fields use
type="password"and values are cleared from memory after use
The app uses Electrobun's typed RPC system for communication between the Bun main process and the React webview:
- Bun-side handlers (webview calls these): Account CRUD, switching, settings, git config, CLI status
- Webview-side messages (Bun pushes these): Account updates, config changes, switch events
Account data is stored at:
~/Library/Application Support/GitAccountSwitcher/
├── accounts.json # Encrypted account data
└── settings.json # App settings
LaunchAgent (if enabled):
~/Library/LaunchAgents/dev.gitaccountswitcher.launcher.plist
This project was migrated from a native Swift/SwiftUI macOS app (~5,100 lines) to Electrobun, preserving the same functionality, UI, and behavior. The original Swift app used:
MenuBarExtra-> ElectrobunTraySecurity.framework->git credential-osxkeychainSMAppService-> LaunchAgent plist- SwiftUI animations -> Framer Motion spring physics
@Published/@StateObject-> React state + RPC push messages
MIT