Wealthfolio logo Wealthfolio
Download
Releases

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.6+: add-ons now run in sandboxed iframes and talk to Wealthfolio through a brokered API bridge. The examples below reflect the current route rendering, permission, network, and secret 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:

  1. Addon Runtime: This is the sandboxed iframe environment where addons are executed. It manages loading, unloading, route rendering, host dependencies, and context management.

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

  3. 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 React from 'react';
import { QueryClientProvider, useQuery, type QueryClient } from '@tanstack/react-query';
import type { Account, AddonContext } from '@wealthfolio/addon-sdk';
import { createRoot, type Root } from 'react-dom/client';

export default function enable(ctx: AddonContext) {
  let routeRoot: Root | undefined;
  const queryClient = ctx.api.query.getClient() as QueryClient;

  const AccountsViewer = () => {
    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>
    );
  };

  const AccountsWrapper = () => (
    <QueryClientProvider client={queryClient}>
      <AccountsViewer />
    </QueryClientProvider>
  );

  const sidebarItem = ctx.sidebar.addItem({
    id: 'accounts-viewer',
    label: 'Accounts',
    icon: 'puzzle-piece',
    route: '/addons/accounts-viewer',
  });

  ctx.router.add({
    id: 'accounts-viewer',
    path: '/addons/accounts-viewer',
    render({ root }) {
      routeRoot ??= createRoot(root);
      routeRoot.render(<AccountsWrapper />);
    },
  });

  ctx.onDisable(() => {
    sidebarItem.remove();
    routeRoot?.unmount();
    queryClient.clear();
  });
}

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
  • Development server configuration
  • Example code and documentation
┌─────────────────────────────────────────────────────────────────────┐
│                      Your Development Machine                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────┐              ┌─────────────────────────────┐   │
│  │   Dev Server    │              │       Wealthfolio App       │   │
│  │                 │              │                             │   │
│  │ localhost:3001  │◀────────────▶│   Auto-discovery &          │   │
│  │                 │   Hot Reload │   Live Integration          │   │
│  │ • /health       │              │                             │   │
│  │ • /manifest     │              │ ┌─────────────────────────┐ │   │
│  │ • /addon.js     │              │ │      Your Addon         │ │   │
│  └─────────────────┘              │ │     Running Live        │ │   │
│           │                       │ └─────────────────────────┘ │   │
│           │                       └─────────────────────────────┘   │
│  ┌─────────────────┐              ┌─────────────────┐               │
│  │   Your Code     │              │   CLI Tool      │               │
│  │                 │              │                 │               │
│  │ src/addon.tsx   │◀─────────────│ • Scaffold      │               │
│  │ components/     │    generates │ • Templates     │               │
│  │ pages/          │              │ • Project Gen   │               │
│  └─────────────────┘              └─────────────────┘               │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

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.

Tags

Add-ons
Open Source
Extensibility
Software Architecture
User Empowerment
← Back to The Blog
Published August 19, 2025