Banger
Blog Download

Inside the Banger Local Runtime

4 min read · Published February 6, 2026

The Banger desktop app is not just a window around remote APIs.

It has to sync mailboxes, maintain local state, coordinate Gmail history, stage attachments, serve local commands, and expose controlled bridges for AI-assisted workflows. If all of that lived directly inside the UI process, the architecture would be harder to reason about and easier to break.

So Banger has a Rust local runtime.

The runtime is the local Rust service boundary shared by the desktop app and trusted local integrations. It is the piece that lets Banger behave like a real desktop system instead of a collection of disconnected tools.

Why a local runtime exists

The UI should be responsible for interaction. It should render state, accept user input, and keep the product responsive.

It should not be the only owner of long-running operational work.

Banger has background responsibilities that outlive a single screen:

  • mailbox sync
  • user-state refresh
  • Gmail history sync
  • provider update processing
  • local database access
  • attachment staging
  • runtime health checks
  • local command handling

A local runtime gives those responsibilities a stable home.

That also lets trusted local clients reuse the same system instead of each one opening its own partial connection to the world.

The runtime is a boundary

The most important part of the runtime is not that it is local. It is that it is a boundary.

Boundaries make product systems easier to reason about. The UI can ask for mailbox state without knowing how every sync pass works. Local tools can request operations through a constrained bridge instead of receiving direct access to all internals.

That boundary gives us a place to handle product concerns like:

  • service ownership
  • logging
  • failure recovery
  • runtime health
  • user and workspace context
  • permissioned operations

When a local app has multiple consumers, a boundary like this becomes more valuable over time.

Local clients share one surface

Banger has a few important local consumers:

  • the desktop app
  • the desktop CLI
  • local AI tooling

They are not identical, but they need access to overlapping capabilities.

The desktop app needs live state and operational commands. The CLI needs administrative and debugging workflows. Local AI tooling needs a safe way to inspect or propose work through Banger without bypassing user controls.

The local runtime lets those clients converge on the same state and the same permissioned operations. In the current architecture, the Rust client SDK gives those clients a shared contract for talking to the service.

That matters because email state is easy to corrupt if every tool invents its own path. A single local runtime surface helps keep app and local tooling behavior aligned.

Local boundaries matter

Local integrations need a boundary without turning the app into a black box.

The runtime keeps local access explicit, scoped, and tied back to the product’s user and workspace model. A local tool should not get direct, unconstrained access to private files or internal state just because it runs on the same machine.

That boundary matters because the app is handling sensitive email, attachments, and team workflow.

Startup is part of the product

Local runtime startup sounds like implementation detail until it fails.

The desktop app needs a reliable path to usable mailbox state. When the runtime starts cleanly, users get a faster path into the inbox and fewer confusing recovery states.

That gives the UI a faster path to useful state and keeps recovery behavior predictable.

Compatibility prevents confusing failures

The local app and runtime evolve together, but they can still get out of sync during development, upgrades, or partial restarts.

The runtime boundary gives the app a clean place to detect mismatch and recover rather than failing later in a confusing way.

This is one of those details that only becomes obvious after you have lived with local services. A clear boundary is cheaper than debugging impossible states later.

What the runtime owns

The runtime owns the work that should be stable, shared, and operational:

  • mailbox sync
  • local mailbox database access
  • Gmail history coordination
  • user-state synchronization
  • attachment staging
  • local command handling
  • service health

The UI owns presentation and interaction. The runtime owns the work that makes the product state exist.

That split keeps the UI responsive and gives trusted local integrations a real foundation.

AI tooling needs the runtime boundary

AI tooling is a good example of why the runtime exists.

AI tooling should not get direct, unconstrained access to every local file or internal database. It should talk to a narrow local surface that understands Banger concepts: mailboxes, threads, approvals, and agent permissions.

The runtime gives local AI tools that bridge.

It also keeps the security model consistent. Agent actions can become proposals. Sensitive operations can require approval. Credentials can remain scoped and revocable. Local tooling can integrate deeply without turning into a side door.

A local runtime makes the app feel native

The user does not care about runtime boundaries directly. They care that the app opens quickly, syncs reliably, keeps state consistent, and does not freeze when work happens in the background.

The runtime is one of the reasons we can aim for that.

It lets Banger behave less like a tab and more like a native email system: stateful, local, responsive, and connected to the operating system around it.

That is the architecture we want for work email. The inbox is operational infrastructure, so the app needs local infrastructure too.

Written by