Build BoltAI
KINDAback to the verdict
An AppKit application that streams from several providers behind one interface, keeps a searchable history that stays fast at ten thousand messages, handles attachments, reaches you anywhere through a global panel that captures the selected text, and reports what each conversation cost.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree if you own one
Why The point of this build is native AppKit behaviour, which exists nowhere else.
Get it Apple menu, then About This Mac.
Verify
About This Mac reports macOS 13 or higher - installfree
Why Builds the application and provides Instruments, which you will need for the streaming and scrolling work.
Get it Mac App Store or developer.apple.com/xcode. open ↗
Verify
xcodebuild -version prints 16 or higher - API keypay per token; a development session is typically cents
Why Phase 3 proves the abstraction, and one provider does not. Two hosted plus a local endpoint is the real test.
Get it Create an account with two providers and generate a key for each, setting a spend limit at the same time.
Verify
Both stream a response through the same code path - installfree, uses your own memory
Why The third provider, and the one that proves the abstraction is real rather than two hosted APIs that happen to look alike.
Get it Download from ollama.com and pull a model sized for your machine. open ↗
Verify
The local endpoint streams through the same code path - account99 USD per year
Why Product phase only. Without it there is no notarization and a warning on other machines.
Get it Enrol at developer.apple.com/programs. open ↗
Verify
A Developer ID Application certificate appears in Xcode settings
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- providers(id, name, base_url, keychain_ref, default_model, enabled) - conversations(id, title, provider_id, model, created_at, updated_at, archived, system_prompt) - messages(id, conversation_id, ordinal, role, content, attachments_json, created_at, prompt_tokens, completion_tokens, cost_usd) - messages_fts · full text over message content, updated on insert - presets(id, name, system_prompt, model, temperature, capture_kind) · capture_kind is none, selection or clipboard - attachments(id, message_id, path, kind, bytes, extracted_text) - API keys live in the keychain and are referenced, never stored in the database.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ~/Library/Application Support/Chat/chat.db | Conversations and messages. Contains everything you have discussed, so treat backups accordingly. |
DEFAULT_PROVIDER | required | local | Which provider is active on launch. Local means nothing leaves the machine. |
STREAM_FLUSH_HZ | required | 60 | How often buffered tokens are flushed to the view. Frame rate, not token rate. |
HISTORY_PAGE_SIZE | required | 80 | Messages loaded per page in a conversation. |
COST_WARN_USD | required | 1.50 | Conversation cost at which a warning appears. |
SPARKLE_FEED_URL | optional | https://example.com/appcast.xml | Product phase only. |
The build, in order
Streaming that feels native
A response streams in smoothly and the window stays responsive while it does.
Server-sent events over URLSession, parsed incrementally off the main thread.
Files
Providers/Stream.swiftBuffer tokens, flush on a display link. This single decision is most of the perceived speed.
Files
ChatView.swiftCancel the task and the request. Stopping the display while tokens keep arriving still costs money.
Files
Providers/Stream.swift
done when · tick each as it passeswatch out- Appending each token directly to the text view forces a full layout pass per token, which pins the main thread and makes a native application feel worse than a web one. Buffer tokens and flush on a display-linked interval, so layout happens at frame rate rather than at token rate.
History that stays fast
Ten thousand messages load, scroll and search without a pause.
Load the visible window of messages, not the whole conversation.
Files
Store.swiftUpdated on insert. Search is how you actually navigate a large history.
Files
Search.swiftTen thousand messages across several conversations, before any optimisation.
Files
Fixtures.swift
done when · tick each as it passeswatch out- Loading an entire conversation into a single text view works until a conversation is long, then costs seconds and hundreds of megabytes. Page the message list and render rows, which is what a native list view is for and is the reason to write this natively at all.
Several providers, one interface
Switching provider or model is one action and costs nothing structural.
Streaming, model list, token usage and a normalised error type. Everything provider-specific stays inside the adapter.
Files
Providers/Provider.swiftTwo hosted and one local. The third proves the abstraction rather than just implementing it twice.
Files
Providers/Referenced from the database, never stored in it. Inspect the database file to confirm.
Files
Keychain.swift
done when · tick each as it passeswatch out- Providers differ in how they report errors, count tokens and format streaming chunks, and letting those differences leak into the interface layer means every provider addition touches the whole application. Normalise inside the adapter, including errors, so the rest of the application sees one shape.
Reaching it from anywhere
A hotkey brings up a panel that already knows what you were looking at.
A non-activating panel so the previous application keeps its context.
Files
QuickPanel.swiftClipboard needs no permission. Request Accessibility only when a selection preset is used.
Files
Capture.swiftWrite, send Cmd-V, restore the previous pasteboard after a delay if your value is still there.
Files
PasteBack.swift
done when · tick each as it passeswatch out- Reading the selected text from another application requires Accessibility, which is a serious permission. Offer the clipboard route first, which needs nothing, and request Accessibility only when a preset that needs the selection is actually used, naming the preset.
Attachments and cost
You can send a file and you always know what a conversation has cost.
Check the model's capability first and fail clearly rather than sending something it will reject.
Files
Attachments.swiftIncluding context tokens, which is the number that explains the bill.
Files
Cost.swiftCompare your total with the provider's dashboard for the same window. Investigate a gap.
Files
Cost.swift
done when · tick each as it passeswatch out- Sending a long document as context on every turn re-sends it with each message, so a twenty turn conversation pays for the document twenty times. Show the per-turn token count including context, because that number is what explains a bill that looks impossible.
Signing, distribution and what leaves the machineproduct builder
Others can install it and know exactly where their conversations go.
Hardened runtime, Developer ID, notarytool, stapler.
terminalxcrun notarytool submit Chat.zip --keychain-profile AC --wait
In the window, not buried in settings. It is the most important fact on screen.
Files
ProviderBadge.swiftA configuration that sends nothing, verified with networking off.
Files
README.md
done when · tick each as it passeswatch out- A chat client's whole risk surface is that message content goes to a provider, and users assume a native application is more private than a web one. Say exactly which provider receives what, and make it visible in the window rather than only in settings.
That is the whole plan for BoltAI. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- The tuned native behaviour of the commercial application.
- Context-capturing workflows that return results in place.
- Model Context Protocol server support.
- Cross-device conversation sync.
- AppKit-backed performance and responsiveness across large workspaces and conversation histories
- carefully tuned keyboard navigation, focus, menus, panels, windows, drag and drop, and other native Mac behavior
- AI Workflows that capture context from other applications and return formatted results where the user is working
- Instant Chat and Instant Dictation as reliable system-wide workflows rather than ordinary in-app screens
- maintained provider compatibility, Cloud Sync, mobile continuity, imports, backup recovery, and protected credential portability
- Conversation branching, which is cheap once messages are ordinal rather than a chain
- A prompt library shared as a file rather than a service
- Per-provider cost comparison for the same prompt, which BYOK makes possible
Need the files? The project pack on the verdict page hands your agent the whole brief · more ai assistants.