Introducing Wealthfolio Add-ons
Opening Wealthfolio to add-ons: a safe, scoped way to build and share custom features while the core stays fast, private, and uncluttered.
Update for Wealthfolio 3.7+: add-ons run in sandboxed iframes, talk to Wealthfolio through a brokered API bridge, and can load private packaged assets. The examples below reflect the current route rendering, permission, network, and asset model. For the full contract, see the
add-on developer docs
.
A year ago, I built Wealthfolio, a simple desktop investment tracker that works offline. Today, with more than ~6,000 users, ~1,000 who paid to support the app, and more than ~5,000 GitHub stars, I’m faced with an interesting problem: everyone wants something slightly different. Each feature request makes perfect sense for that person, but would clutter the app for everyone else and make it difficult to maintain.
So instead of building every possible feature, I’m doing something different. I’m making Wealthfolio extensible through an add-ons system.
The Logic of Letting Go
The old model assumes the creator knows best; you build something and guard its purity. This is especially true for me since Wealthfolio is my side project, something I need to craft and shape as I want it to be. But personal finance is deeply individual. The way you track investments depends on your age, tax laws, risk tolerance, and countless other factors. No single app can capture all these variations without becoming bloated or requiring a large team.
Freedom Through Constraints
Add-ons work because they’re constrained. They can’t break the core app. They can’t access data they shouldn’t. They solve specific problems without creating general chaos. This constraint is liberating for both users and developers. Users get exactly the functionality they need without paying the complexity cost of features they don’t use. Developers can experiment with ideas that would be too risky or niche to include in the main application. It’s a different kind of democracy than the usual “feature request” model. Instead of voting on what should be built, people build what they need. The useful add-ons find their audience. The rest disappear without cluttering the experience for everyone else.
A perfect use case of “Vibe Coding”
There’s a shift happening in how people relate to code. We call it “vibe coding”. It’s the idea that you can describe what you want in plain language and let AI handle the implementation details. You focus on the creative intent, the “vibe” of what you’re building, rather than wrestling with syntax and boilerplate. This shift is enabled by powerful language models that can translate your ideas into working code, clearer documentation that helps you articulate what you need, and platforms that make it easy to share and extend existing work. More importantly, it’s enabled by a mindset shift: coding becomes less about memorizing APIs and more about clearly expressing your intent.
When someone with a specific need can spend an afternoon building a small add-on that solves their exact problem, then share it with others who have the same problem, something interesting happens. The software becomes more useful without becoming more complex for anyone who doesn’t need that specific feature.
Architecture Overview
Wealthfolio’s architecture is designed to support a wide range of addons while maintaining security and performance. At a high level, the system consists of three main components:
-
Addon Runtime: This is the sandboxed iframe environment where addons are executed. It manages loading, unloading, route rendering, host dependencies, and context management.
-
Permission System: This component ensures that addons can only access the data, host APIs, and network destinations they’re explicitly allowed to use. It includes manifest declarations, static detection, install-time approval, and runtime enforcement.
-
API Bridge: The brokered API bridge provides a type-safe interface for addons to interact with Wealthfolio’s core functionality without receiving raw host state. It covers portfolio data, accounts, activities, market data, UI navigation, scoped secrets, brokered HTTPS requests, and more.
┌─────────────────────────────────────────────────────────────────┐
│ Wealthfolio Host Application │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Addon Runtime │ │ Permission │ │ API Bridge │ │
│ │ │ │ System │ │ │ │
│ │ • Load/Unload │ │ • Detection │ │ • Type Bridge │ │
│ │ • Lifecycle │ │ • Validation │ │ • Domain APIs │ │
│ │ • Context Mgmt │ │ • Enforcement │ │ • Scoped Access │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Individual Addons │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Addon A │ │ Addon B │ │ Addon C │ │ Addon D │ │
│ │ Portfolio │ │ Custom │ │ Market │ │ Tax │ │
│ │ Analytics │ │ Alerts │ │ Data │ │ Reports │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Basic Addon Structure
An addon exports an enable(ctx) function. Wealthfolio loads it in a sandboxed iframe and passes a context object with brokered access to financial data, the UI system, route rendering, event listeners, scoped secrets, and network requests.
import { QueryClientProvider, useQuery, type QueryClient } from '@tanstack/react-query';
import type { Account, AddonContext } from '@wealthfolio/addon-sdk';
let addonCtx: AddonContext | undefined;
function AccountsViewer() {
const ctx = addonCtx!;
const { data: accounts = [], isLoading } = useQuery<Account[]>({
queryKey: ['accounts'],
queryFn: () => ctx.api.accounts.getAll(),
});
if (isLoading) return <div className="p-6">Loading accounts...</div>;
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-4">Accounts</h1>
{accounts.map((account) => (
<div key={account.id} className="border rounded p-4 mb-2">
<div className="flex justify-between">
<span className="font-semibold">{account.name}</span>
<span>{account.currency}</span>
</div>
</div>
))}
</div>
);
}
function AccountsRoute() {
const queryClient = addonCtx!.api.query.getClient() as QueryClient;
return (
<QueryClientProvider client={queryClient}>
<AccountsViewer />
</QueryClientProvider>
);
}
export default function enable(ctx: AddonContext) {
addonCtx = ctx;
// The matching route and sidebar link are declared in manifest.json.
ctx.router.add({
id: 'accounts-viewer',
path: '/addons/accounts-viewer',
component: AccountsRoute,
});
ctx.onDisable(() => {
addonCtx = undefined;
});
}
The QueryClient above belongs to this addon sandbox; its cache is not shared with the host or other addons. Invalidation and refetch calls are mirrored to the host, while host-originated changes reach the addon through domain event subscriptions.
Wealthfolio 3.7 also provides ctx.assets for images, fonts, media, configuration, and Wasm packaged under assets/** or dist/assets/**. Non-JavaScript/CSS files in those roots stay private to the addon and load as verified Blobs or lifecycle-scoped Blob URLs; JavaScript and CSS remain runtime modules and styles.
Permission System
The permission system works in stages: the manifest declares what the addon needs, static code analysis detects API usage patterns, Wealthfolio categorizes the requested access by risk level, and the user approves or rejects the installation. If an addon asks for network access, the install/update flow also shows the exact HTTPS hosts it wants to reach.
Installation Flow:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ │ │ │ │ │ │ │
│ ZIP File │───▶│ Extract │───▶│ Validate │───▶│ Analyze │
│ │ │ │ │ Manifest │ │ Permissions │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
│
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ │ │ │ │
│ Running │◀───│ Enable │◀───│ Sandbox │◀─────────────┘
│ Addon │ │ │ │ Load │
└─────────────┘ └─────────────┘ └─────────────┘
Secrets are stored in the OS keyring under the addon’s own scope. Addons do not receive raw access to another addon’s secrets. For external APIs, addons use the brokered network.request API and can ask Wealthfolio to inject a bearer token from a scoped secret, so authorization headers do not have to be assembled in frontend code.
Development Experience
The addon system is designed to feel familiar to anyone who’s built modern web applications. It’s TypeScript and React all the way down—no proprietary languages or frameworks to learn.
The development workflow is straightforward:
# Create a new addon using the CLI
npx @wealthfolio/addon-dev-tools create <addon-name>
# Navigate to the generated project
cd <addon-name>
# Start the hot-reload development server
pnpm dev:server
# Your addon appears in Wealthfolio automatically
# Edit your code, see changes instantly
We provide a CLI tool that scaffolds a complete addon project with:
- TypeScript configuration
- React components setup
- Permission manifest template
- Host dependency configuration for React, the SDK, and Wealthfolio UI
- Private packaged-asset support
- Development server configuration
- Example code and documentation
Your source ──watch/build──▶ localhost:3001 ──discovery/hot reload──▶ Wealthfolio
├─ /health
├─ /runtime-package
├─ /runtime-files
└─ /runtime-assets/:id?generation=...
Wealthfolio 3.7 requires @wealthfolio/addon-dev-tools 3.7 or newer for live development. The server publishes immutable runtime generations so code, CSS, and lazy asset bytes always come from the same build.
Wrapping Up
Perhaps this is what boring software really means. Not software that lacks features, but software that stays out of your way while enabling you to build exactly what you need on top of it.
Wealthfolio still does one thing: it tracks your investments locally, privately, simply. But now it also does something else: it gets out of the way when you need it to do more.
This feels like a small thing. Maybe it is. But small things compound. And in a world increasingly dominated by platforms that want to control every aspect of your digital life, the ability to modify your tools to fit your needs feels increasingly radical.
The add-ons are now available, and the current developer docs live at wealthfolio.app/docs/addons. We’ll see what people build.