Engineering Standalone Tale • July 15, 2026 • Finance, Rust, Tauri, React, Local-First, macOS, Open Source • 14 min read

Worthweave: The Long Way to a Trustworthy Number

The story of turning inconsistent broker exports into a private, local-first macOS portfolio application, and why trustworthy financial software starts with admitting what it does not know.

Broker records being woven into a coherent portfolio chart inside a desktop application

The first useful thing Worthweave told me was that Worthweave was wrong.

I had imported years of activity from Interactive Brokers and Trading 212. The interface was polished, the accounts were present and the portfolio card contained a reassuringly precise number. It was also plainly not my portfolio value.

That moment changed the project. A conventional product demo might have hidden the awkward data, rounded the total and moved quickly to screenshots. Financial software does not get that luxury. If an application cannot explain where a number came from, what is missing and which assumptions were made, then visual confidence becomes a liability.

So the suspicious total became the real beginning of Worthweave: a private, local-first macOS application for bringing investments from multiple brokers into one coherent view.

Benjamin Franklin is credited with the line, “An investment in knowledge pays the best interest.” Worthweave is, in part, an attempt to make that knowledge dependable.

One Number, Several Truths

The product brief sounded simple: import broker exports, reconstruct holdings, add current prices, convert everything into a reporting currency and show the total.

Each verb concealed a different systems problem.

  • Import meant recognising several CSV dialects, section layouts and schema changes across years of exports.
  • Reconstruct meant understanding buys, sells, splits, transfers, warrants, dividends, fees and broker-specific sign conventions.
  • Add current prices meant resolving instrument identity across ticker changes, ISINs, contract identifiers, exchanges and delistings.
  • Convert meant distinguishing a fresh headline exchange rate from the historical rate required on the date of each cash flow.
  • Show the total meant refusing to turn missing values into zero merely because zero is convenient to add.

The earliest false totals exposed several classes of mistake. A position sold long ago could reappear because a broker row had been interpreted with the wrong quantity direction. A British price quoted in pence could be treated as pounds. Current broker positions could disagree with a transaction replay because the available export did not contain the whole acquisition history. A ticker-looking value could actually be an ISIN. A corporate action could change quantity without behaving like a trade.

None of these are exotic edge cases to the person whose money is being described. They are the portfolio.

A Broker Export Is Not a Ledger

CSV creates a dangerous illusion of simplicity. It is plain text, opens in a spreadsheet and appears to contain rows of transactions. That does not make it a canonical financial record.

Trading 212 and Interactive Brokers describe activity differently. Even one broker can expose trades, cash movements, open positions and corporate actions in separate sections with different identifiers. Exports overlap. Users download the same date range twice. Instrument symbols change. Some files contain current positions; others contain only events from which positions must be derived.

Worthweave therefore treats every broker format as an adapter into a small canonical event model. Parsing happens in Rust, inside a bounded and account-aware import boundary:

Broker CSV
    -> broker-specific parser
    -> schema and account validation
    -> canonical immutable events
    -> atomic SQLite transaction
    -> deterministic portfolio views

Every event belongs to an explicit broker account, jurisdiction and legal account type. An ISA is not allowed to blur into a general investment account merely because both happen to contain the same ticker.

The importer reads at most 50 MiB and 500,000 rows, validates before mutation, and commits each file atomically. A SHA-256 content hash catches exact repeats, while broker transaction identifiers catch overlapping exports. Re-importing a known file is not simply rejected. It can follow an idempotent repair path that refreshes instrument links, current-position snapshots and broker price data without duplicating source events.

That last detail mattered during development. Fixing an importer should not force somebody to delete their portfolio and begin again. The application needed to become more correct around data that was already present.

Current Quantity and Historical Cost Are Different Questions

Interactive Brokers position sections provided an important distinction.

The latest broker snapshot is authoritative about what the account holds now. Transaction history explains how it got there, but only if the exported period is complete. Worthweave persists those broker positions as immutable, account-scoped snapshots and reconciles them against the transaction replay.

If the snapshot says 125 shares and the visible history explains none of them, the application can still show the current quantity. It cannot honestly manufacture an average cost or realised return. If the replay differs by a fraction, the interface reports the mismatch and uses the broker quantity for the current holding while keeping cost-history coverage explicit.

This led to a more useful vocabulary in the interface:

  • Current broker quantity answers what is held now.
  • Cost basis available means imported history or broker data can support the acquisition value.
  • History quantity differs identifies a reconciliation issue without discarding the broker snapshot.
  • Cost history incomplete says exactly which calculation must remain partial.

The same principle shaped corporate actions. Early fixes for splits and transformed instruments could have been written as one-off symbol exceptions. That would have solved my dataset while quietly planting the next user’s bug. They were replaced with a generic corporate-action adjustment layer driven by imported broker events and stored instrument metadata. Symbol-specific patches are seductive because they make a test turn green. A durable financial model needs rules that survive unfamiliar symbols.

Exact Arithmetic or No Arithmetic

Binary floating point is excellent for graphics and scientific approximation. It is a poor source of ledger truth.

Worthweave stores financial values as exact scaled integers: a signed coefficient and a decimal scale. Quantities, prices, exchange rates and cost basis keep their source precision. Cash is normalised to the currency’s minor unit only when that conversion is lossless. If a broker supplies more precision, Worthweave retains it rather than silently rounding it away.

Conceptually, the value 123.4500 is not stored as an approximate IEEE 754 number. It is represented as coefficient 1234500 with scale 4. Display formatting can later choose two decimal places for a pound total without rewriting the ledger value.

This separation solved two different problems. It prevented arithmetic drift across long transaction histories, and it stopped interface decisions from contaminating source data. A portfolio card should display £69,790.30, not expose four internal decimal places. The calculation can remain exact while the presentation remains humane.

Missing Is a First-Class Value

The hardest product decision was also the most important: incomplete data must remain incomplete.

Many portfolio tools collapse absence into zero. A holding without a price contributes nothing, producing a total that looks complete but is merely smaller. Worthweave instead calculates a clearly labelled valued so far subtotal and reports how many holdings, prices or currency pairs are still missing. A complete portfolio snapshot is not written until every open holding can be valued in the reporting currency.

That rule extends to returns. The attribution engine replays average-cost lots and separates:

  • realised gains;
  • unrealised gains;
  • dividends;
  • interest;
  • fees;
  • taxes; and
  • currency movement.

Deposits and withdrawals are external cash flows, not investment performance. A foreign dividend needs the exchange rate from its payment date, not merely today’s rate. Currency movement needs transaction-date FX and enough cost history to compare the acquisition and valuation effects. Where those inputs are incomplete, the component is partial or unavailable and the application explains why.

This can feel less impressive than filling every card with a number. It is substantially more useful.

Market Data Without Magical Thinking

The first imports left dozens of holdings without prices. Some were US equities, some were UK-listed instruments, some had changed identity and a few were genuinely delisted. Treating every failed lookup as “delisted” was obviously wrong.

Worthweave now uses a layered market-data approach:

  1. Prefer a mark price supplied by the latest broker position file.
  2. Use an optional Massive integration for supported US instruments.
  3. Fall back to a bounded public-market lookup for UK and other supported symbols.
  4. Preserve manual prices for instruments no provider can resolve.

Provider responses are cached locally. Requests are bounded by timeouts, batched where possible and designed to fail into a comprehensible stale or missing state. A provider outage must not turn into a cryptic modal or make the rest of the portfolio inaccessible.

Foreign exchange follows a similar model. Official ECB reference rates are fetched over a fixed HTTPS endpoint, size-bounded and cached locally. Cross-rates are derived through EUR using exact decimals. Historical transaction dates are backfilled, with the prior available business-day rate used where weekends or holidays contain no publication. Manual rates remain available as explicit overrides.

Freshness is provenance, not decoration. Prices and rates retain observation time and source. Worthweave can say that a price is broker-provided, manually entered, delayed, stale or unavailable. It does not quietly pretend that all numbers arrived from the same clock.

The Chart That Had to Earn Its Curve

A lifetime portfolio chart seems like an obvious feature. It was missing from the first version because historical value is not just a line drawn through today’s holdings.

To make it meaningful, Worthweave needed dated positions, historical prices, dated FX rates and account-aware cash flows. It also needed to cope with days when the application was not running. Saved valuations extend when the application next opens, while imported broker history and historical market data provide the best available reconstruction for earlier periods.

The first chart was technically a chart and aesthetically a thicket. Hundreds of visible point markers made it look as though the line had grown scales. Relative-period changes produced implausible percentages because the headline compared against the wrong baseline. Missing daily prices created sudden dives that looked like market crashes.

The repaired chart treats date windows as views over one valuation series, calculates change from the first valid point inside the selected period and avoids converting missing observations into zero. It groups the full portfolio, broker and individual account views behind connected tabs. Loading those views is asynchronous, cached and visibly acknowledged rather than leaving the application looking frozen.

The result is not merely prettier. It has better financial semantics.

Worthweave portfolio page showing fictional holdings, a portfolio growth chart and account filters

A Calm Interface Is an Engineering Feature

The visual language of Worthweave is deliberately quiet: deep green, warm off-white, restrained status colours, Manrope for display and Inter for dense financial information. The theme was never the problem. Consistency was.

Real usage exposed modals aligned to the window edge, native selects that were too short beside text fields, searchable dropdowns whose popovers collided with neighbouring controls, double scrollbars, tiny typography on a high-resolution display and tables that repeated an account column while already filtered to one account.

Each issue was small in isolation. Together they made the software feel less certain than the calculations beneath it.

The repair work produced shared modal geometry, centred overlays, consistent field heights, proper toggle controls, connected provider and account tabs, search within holdings and a stationary navigation rail with one content scroller. Portfolio loading gained an application-branded progress state. Disabled private AI became visibly disabled, with a clear route to Settings rather than an apparently broken question box.

This is why interface polish belongs in the reliability story. When an operation legitimately takes time, silence resembles failure. When a warning is technically accurate but poorly named, it resembles a data problem. Good UX is how the system communicates its state without asking the user to reverse-engineer it.

Worthweave overview using an isolated fictional showcase portfolio

AI on a Short Leash

Worthweave includes optional local AI through Rapid-MLX or Ollama. The word optional does substantial work here.

Portfolio calculations remain deterministic Rust code. The model receives a bounded JSON projection containing application-calculated valuation, allocation, income and reconciliation data. It may explain those facts. It cannot create transactions, alter holdings, predict prices or fill missing values with plausible prose.

The local endpoint must resolve to loopback. Questions and responses are size-bounded, broker text is treated as untrusted prompt data, and the model is instructed not to provide personalised financial advice. Model output is not persisted into the ledger.

Runtime behaviour also needed engineering. A model chosen from the laptop’s headline memory could still be too large for the memory actually available. Startup could take longer than an optimistic twenty-second deadline. A runtime started by Worthweave could remain alive after the application closed.

The final lifecycle management tracks ownership: Worthweave starts the configured runtime only when needed, probes it with short health bounds, chooses a model tier conservatively, waits with a clear progress state and terminates only a process that Worthweave itself started. A runtime launched independently by the user is left alone.

The same caution now shapes the answer UI. Free-form model text is parsed into readable sections inside a centred result modal, but deterministic concentration metrics remain the authority. If the application knows that NIO is not ninety per cent of the portfolio, a fluent model does not get to overrule it.

Local-First Means More Than No Login

Worthweave is a Tauri 2 application, not a hosted dashboard wrapped in a desktop window. Rust owns the ledger, broker adapters, exact calculations, SQLite persistence, backup encryption and native network integrations. React, TypeScript, TanStack Query and Zod own the interface and validate the narrow command boundary.

Broker files -> Rust adapters -> SQLite ledger -> deterministic reports
                                          |             |
                                          |             +-> local AI explanation
                                          +-> typed Tauri IPC -> React interface

There is no local web server. The webview has a restrictive content security policy and a narrow IPC allowlist. The application directory is owner-only, the SQLite file is mode 0600 on macOS, and bundled fonts avoid remote requests.

Readable JSON exports are versioned. Complete backups use age passphrase encryption, are written through owner-only temporary files and are validated for authentication, size, SQLite integrity, schema compatibility and foreign-key consistency before they can replace live data. The restore control requires explicit acknowledgement because “restore” is, in fact, destructive.

Local-first is not an aesthetic preference. For financial records, it is a threat model.

Performance Is Part of Trust

At one stage the Portfolio page took long enough to open that the application appeared to have crashed. Switching from the full portfolio to one broker rebuilt too much history. A correct result that arrives behind a frozen interface still feels unreliable.

The database gained projection, activity, import and latest-position indexes. Import deduplication moved into SQLite uniqueness constraints rather than loading every historical identifier into memory. Queries became view-specific and deferred until the relevant screen opened. TanStack Query caches account and report results. Historical series are reused across period filters instead of being reconstructed for every click. Backup and export paths stream, so memory no longer scales with the entire database.

Some calculations remain inherently substantial, especially a lifetime series across many instruments and currencies. The interface now says so with a branded loader. Performance work reduced the wait; honest feedback removed the ambiguity.

The Release Was Another Data Pipeline

By the time version 0.1.1 was ready, releasing it deserved the same discipline as importing a broker file.

The source of truth is a dated Keep a Changelog section and three aligned versions across the frontend package, Rust crate and Tauri configuration. An annotated v0.1.1 tag triggered a macOS workflow that:

  1. validated the tag, version and changelog;
  2. ran Rust formatting, 37 unit tests and strict Clippy warnings;
  3. ran frontend unit tests, linting, a production build and Playwright accessibility checks;
  4. audited production JavaScript and Rust dependencies;
  5. imported the Developer ID certificate into an ephemeral keychain;
  6. built the Apple Silicon application and DMG;
  7. enabled the hardened runtime, signed and notarised the application;
  8. verified the code signature, Gatekeeper assessment and stapled notarisation ticket;
  9. generated a SHA-256 checksum and signed updater archive;
  10. published the worthweave Rust crate to crates.io; and
  11. created the GitHub Release and updater manifest.

The first run found that my verifier incorrectly expected a notarisation ticket to be stapled to the DMG, even though Tauri staples the application before packaging it. The second reached crates.io and discovered that an unidentified registry preflight receives HTTP 403. The third passed and published both destinations. An independent download then revealed that the checksum contained a CI workspace path. That, too, was fixed and verified from a clean temporary directory.

These were not glamorous launch tasks. They were exactly the sort of details that distinguish “the build succeeded on my Mac” from a reproducible public release.

The repository also went public with private vulnerability reporting, immutable third-party action pins, dependency review, Dependabot and a master ruleset that blocks deletion and force-pushes while requiring CI. Inter and Manrope ship with their OFL 1.1 notices inside the application bundle. The updater uses a dedicated signing key whose public half is embedded in the application.

Version 0.1.1 is available as a signed GitHub release and as the worthweave crate on crates.io.

What the First Release Really Means

Worthweave 0.1.1 supports account-aware imports from Trading 212 and Interactive Brokers, with Robinhood account models prepared while import schemas await representative fixtures. It can reconstruct holdings, preserve broker snapshots, calculate cost basis and returns where evidence permits, refresh market data and historical FX, chart portfolio history, encrypt backups and explain deterministic analytics through an optional local model.

More importantly, it has a philosophy.

Financial software should be precise without pretending to be omniscient. It should distinguish source data from derived values, current positions from historical explanation, and a missing price from a worthless asset. It should make provenance visible, keep artificial intelligence subordinate to arithmetic and leave the user’s private records on their own machine.

The project began with a number I did not trust. The first release is not a claim that every future broker file will be easy. It is a commitment that when Worthweave knows a number, it can show its workings, and when it does not know, it will say so plainly.

That is the weave: many imperfect threads, one accountable picture.

Worthweave is open source under Apache 2.0. Issues, synthetic broker fixtures and careful contributions are welcome.