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.11.0

Version 1.11.0 ·

Added

  • The writes get their own dispatcher, so an editor's approval rule stops collapsing. The facade's one real cost was that every call reached the client as bastion_call_tool, so a rule covering app_store_connect_list_builds ended up covering ..._update_app too. A profile whose server Bastion can classify is now served a fourth tool, bastion_call_write_tool, and the ordinary dispatcher REFUSES anything Bastion knows to mutate — naming the other one, so the next call succeeds. Allowlisting bastion_call_tool in an editor can no longer run a write, and that is a property Bastion holds up rather than a hint it asserts and hopes the host respects. The two are disjoint in both directions, so nothing downstream has to check the split twice.

    bastion_call_tool still carries no readOnlyHint, and that is deliberate. A tool in neither the manifest's writeTools nor the server's own annotations is UNCLASSIFIED, and the house rule is that silence is not a no. Bastion already bets that way for its own gating, but that bet only decides Bastion's refusal; putting it in an annotation moves it into the editor's confirmation prompt, where being wrong means a mutation nobody was asked about. Refusing the writes it knows is honest and checkable. Claiming to be read-only would be neither.

    Classification comes from the manifest's writeTools ORed with what the server annotates, so a server that says nothing either way keeps exactly the three tools it had rather than gaining a fourth that would be a guess — and a profile with writes off has nothing to dispatch to, so it keeps three as well. Seven catalog entries declare writeTools today; Bastion's own server annotates every tool it exposes.

  • Every client now says what it is actually being sent. Each server pane already quoted its own figure and each one looks survivable alone; a client wired to five of them pays the sum on every connect, and nothing in the app added them up. The Clients pane does now, under Context, and it resolves both axes into the one number that matters: a server loading on demand counts as its three or four declarations rather than its full listing, and a client that defers schemas is told it is sent everything but holds only the names — no alarm, and no false comfort either.

    It says "measured", and names how many of the wired profiles have a figure, because tool-costs.json holds one only for a profile something has actually listed. The total is a floor, and a floor that says so.

Changed

  • "Load tools on demand" moved from the profile to the server. It was stored per profile, with the control on a server writing through to every one of its rows — which is why that control needed a "Mixed" position at all. Mixed was never a state anybody set out to reach; it was the shape of the storage showing through the window.

    The question the switch answers is is this listing big enough to be worth the trade, and a listing is a property of the server: appstore-connect is 85 tools, reddit is 14, and two profiles of one server differ in credentials and in the write gate rather than in whether eighty-five is a lot. The one disagreement that genuinely was per profile — "this one feeds Claude Code, which defers by itself" — is the client axis above, where a profile feeding two clients can be answered honestly instead of averaged.

    A lazyTools already written on a profile is carried onto its server once, on the first launch after upgrading, and the key then leaves profiles.json on the next save. upsert_profile still accepts lazy_tools and now writes it through to the server: an argument that starts being silently ignored is worse than one that was renamed, and the schema says out loud that it moves every profile of that server. list_servers and get_server report it, which is where it lives now.

Fixed

  • The cost figure stopped rounding in its own favour. A measurement now records how many of its tools Bastion could tell were writes, so a view can distinguish "no writes here" from "Bastion cannot tell". Without it ServerDetail had no way to see a server that classifies by annotation alone — the manifest is all a view has — and understated the facade by the fourth declaration on every one of them. The same figure drives a new caveat: where load-on-demand is on and Bastion could classify nothing, the pane says so, because that is the case where one approval rule in the editor still covers every call including the writes.