Skip to content
Buy · $14.99 Opens the GitHub repository in a new tab. The signed build is a separate download; see the README for what is in each.
Changelog

Bastion 1.22.0

Version 1.22.0 ·

Added

  • Claude Code's other config directories are offered as clients of their own. Claude Code reads CLAUDE_CONFIG_DIR, so one Mac can run several profiles side by side with separate server lists — and Bastion knew about exactly one of them, ~/.claude.json. The other was left to be kept in step by hand, which meant copying entries between files and inheriting the first profile's token with them: one revocation signed out both, and nothing on screen said the second had fallen behind. Bastion now finds ~/.claude-<name> directories and gives each a row with its own gateway token, its own audit trail and its own Configure button. Detection is a switch in Settings, beside a list for a config directory that lives somewhere no scan would look. The default profile's file is deliberately not discovered but named outright, because Claude Code keeps it outside its config directory — ~/.claude.json, not ~/.claude/.claude.json, and the latter does exist, holding first-run bookkeeping and no servers at all.

  • A config directory Bastion has not written is left alone until you ask. Automatic rewiring decides what is wired by looking for entries shaped like Bastion's, and never at the token — so a second Claude profile filled in by copying entries out of the first passes that test without Bastion having touched the file. Such a row is now kept out of automatic updates until Configure has been pressed on it once, and says so. Pressing it renames the entries to the current scheme in place, issues that profile its own token, and leaves a backup beside the file. Clients with a single config file are unaffected.

Fixed

  • A window resize could take the app down. AppKit's frame autosave writes from inside the setFrame that prompted it, so a resize SwiftUI drives itself meant writing to UserDefaults in the middle of the window's own layout pass — and that write was enough to abort the process. Persisting posts NSUserDefaultsDidChange, SwiftUI's @AppStorage observer reads it as a settings change and dirties the hosting view, and the constraint update that follows lands inside the layout pass still running; AppKit throws rather than re-enter, and nothing catches it. It needed no bad frame and no bad window — one @AppStorage anywhere in the app was fuel enough. Window frames are now restored with setFrameUsingName and written back on a turn of their own, and only for a resize or a move you performed. A frame remembered by an earlier version still restores.