Skip to content

AWS CLI Gateway 1.0 - A Complete Rebuild

I just shipped v1.0 of AWS CLI Gateway, a macOS menu bar app I built while migrating our engineering team from IAM profiles to IAM Identity Center. The migration itself wasn’t the hard part. The hard part was the daily experience afterward: running aws sso login constantly, forgetting which profile is active, or finding out your session died 20 minutes ago when a deployment fails. I built this to smooth out that transition for my engineers, and it ended up becoming part of their daily workflow for switching between profiles and authenticating across multiple accounts with SSO.

What it does

AWS CLI Gateway sits in your menu bar and monitors your AWS SSO sessions. Click a profile to connect and it tracks the token lifecycle for you. Countdown timers, expiry warnings, automatic credential refresh. When you’re juggling multiple AWS accounts (dev, staging, prod, shared services), you can have up to 5 sessions active at the same time, each with its own timer and health check.

There’s also a gateway CLI command that lets you run AWS commands without specifying --profile every time. If you have one session active, it uses that profile automatically. If you have multiple sessions going, it gives you an interactive picker to select which profile you want to route through.

What changed in 1.0

This is a ground-up rewrite. The old version used a native NSMenu dropdown for the UI and honestly, it was limiting. You can’t put interactive controls in a macOS menu. I replaced it with a floating NSPanel backed by SwiftUI, which gives me expandable profile rows, inline refresh buttons, and real-time countdown updates without the menu flickering every time something changes.

The session monitoring got a lot smarter too. The previous version only watched role credentials in ~/.aws/cli/cache, which expire every hour. The problem is that the underlying SSO session token in ~/.aws/sso/cache has its own 8-hour lifespan. When that dies, the CLI can’t refresh role credentials anymore, but the app was still showing time remaining on the old ones. I started calling these “phantom sessions.” The countdown looks fine, but your next API call fails and you’re left wondering what happened.

Now it tracks both layers independently and runs periodic health checks (sts get-caller-identity) to verify the session is actually alive. If the health check fails, it marks the session expired immediately rather than waiting for the countdown to hit zero.

I also fixed how the app computes SSO cache file hashes. The old logic didn’t match what botocore actually does. It uses sha1(sessionName) for new-style configs and sha1(startUrl) for legacy ones. Getting this wrong meant the app couldn’t find the right cache file for certain profile configurations, which was contributing to the phantom session problem.

Beyond the session work, the codebase got a solid cleanup. I took it from 7,054 lines down to 5,382 with zero features removed. Consolidated duplicate managers into a generic JSONFileStore, extracted shared date parsing, and removed orphaned files from the old UI architecture. Sometimes a rewrite is worth it.

What’s next

I have a few things on the roadmap that I’m working through.

App-to-profile binding. When you’re running terraform against one account and cdk against another, you need per-command profile injection. I want to add the ability to bind specific commands to specific AWS profiles, generating shell functions that inject AWS_PROFILE scoped to just that command. No global exports, no accidental cross-contamination between tools.

Keychain-backed token storage. Right now, SSO tokens live in plaintext JSON files under ~/.aws/sso/cache (that’s just how the AWS CLI works). I want to explore storing refresh tokens in the macOS Keychain instead, so they’re encrypted at rest and protected by your login password or Touch ID. This would also survive cache clears without requiring a full browser re-authentication.

Profile grouping. When you have 15+ profiles across multiple AWS organizations, a flat list doesn’t scale. I want to add organization-level grouping so you can collapse profiles by account or team.

Faster silent refresh. The current refresh path calls sts get-caller-identity to force the CLI to use the refresh token. I’d like to handle the OAuth token refresh directly by calling the SSO OIDC endpoint and skip the CLI subprocess overhead entirely.

Menu bar icon state. The status bar icon should reflect the worst-case session state across all profiles. If any session is about to expire, the icon should change to signal that you need to take a look without having to open the panel.

Try it

Grab the latest from GitHub Releases or build from source:

git clone https://github.com/dmcphearson/aws-cli-gateway.git
cd aws-cli-gateway
./build.sh

Requires macOS 15.2+ and AWS CLI v2. Apache 2.0 licensed.

If you run into any issues or have feedback, open an issue on GitHub. Collaboration makes us all better at what we do.