I built my own operating system — Link-o-Mator
Link-o-Mator

I stopped paying for five apps and built the one I actually wanted.

Three months, 275 commits, one database. A personal operating system that swallows links, tasks and money into a single pipeline — and captures from wherever I already am: the browser, a share sheet, a photo of a receipt, my bank's email, a Telegram message.

link-o-mator.app/money
JULY 2026
Money
CSVScan
Net worth CashIncl. debt
$18,412.60 true net worth
Cash$24,890.15
Debt−$6,477.55
Income$9,400.001 more due
Spent$5,138.72$221.83/day
Left over$4,261.2845% of income
Fixed costs$1,845.0011 commitments
On this pace$6,875.00by Jul 31
Set aside$1,200.00marked savings
Safe to spend today
$174.20 for each of 8 days left
$312.40 ahead of budget pace on day 23
Where it went
Groceries$742 / $800
Eating out$611 / $450
Software$389 / $500
Transport$204 / $350

Money — Overview. Every figure on this screen is derived, not typed. Net worth reads from account balances plus valued crypto holdings; "on this pace" projects the month from elapsed days; "safe to spend" is expected income minus commitments minus what's already gone, divided by the days that remain. Mockup data.

Commits
275
Tests green
619
Lines TS/TSX
31,088
DB entities
23
Screens
28
Surfaces
4

Counted on 9 Aug 2026 from git rev-list, vitest run (69 files, 0 failures) and the InstantDB schema. 186 of those commits are features, 46 are fixes. The rest is plumbing.

01 — The problem I actually had

Three graveyards, one root cause

I run a voice-AI and staffing company out of Costa Rica, plus a personal brand on the side. By the start of this year I had three dead systems.

A link graveyard: 1,400-odd saves scattered across Instagram bookmarks, browser tabs, a Notion database I'd stopped feeding, and a Telegram "saved messages" chat. Nothing was tagged. Nothing was findable. I was re-searching for the same competitor ad three times a month.

A task app I never opened, because opening it meant a context switch away from whatever produced the task in the first place.

And money I couldn't see. Two currencies, five accounts, a crypto wallet, business and personal bleeding into each other. Every budgeting app assumes you live in a country Plaid covers. Costa Rica isn't one. So the honest options were: type every transaction by hand, or fly blind.

Different symptoms, same disease. Capture was expensive and nothing was connected. Saving a thing cost more attention than the thing was worth, so I stopped saving. And once the pieces lived in five products, no single one of them could ever answer a question that spanned two.

The competitor ad I saved, the task to write a response to it, and the $340 I spent testing it are three facts about one project. Five apps meant nobody knew that.
02 — The bet

One database. Three pillars. Capture from everywhere.

The design brief fit in a sentence: make saving free, and put everything in the same place.

Free meaning zero friction — I should never have to open the app to feed it. Same place meaning literally one database, not three products with an integration bolted between them. Links, tasks and transactions are separate pillars in the UI, but they share a schema, a tag vocabulary, a project taxonomy, and one AI worker that classifies all three.

The app switches pillars from a single control in the sidebar. Everything else — search, tags, projects, capture, settings — carries across.

Pillar one

Links

Save a URL from anywhere. It gets scraped, enriched, summarised, tagged and filed without a form.

Pillar two

Tasks

Type a sentence, get a dated task. Today, Week, Upcoming, Projects. Swipe to complete.

Pillar three

Money

Statements, receipt photos, bank emails and manual entries — all through one pipeline.

link-o-mator.app/library
Library
1,412 saves · 96% enriched
GallerySave
COST PER LEAD $41 $9 REEL0:41
Interstaff · Instagram
The hook that took our dental clinic ad from $41 to $9 CPL
#meta-ads#hooks+ ad-creative
TURN 3 VOICE AGENT TEARDOWN THREAD12.4K
High Level · X
Why every voice agent demo fails on the third turn — a teardown
#voice-ai#competitor
BROWSER SERVER META CAPI SURVIVES ATT VIDEO14:02
Interstaff · YouTube
Building a CAPI server-side pipeline that survives iOS privacy
#capi#automation
ARTICLE
Personal · Web
A field guide to reading financial statements you didn't write
#learning+ business
1 / 9 SWIPE → CAROUSEL3.1K
High Level · LinkedIn
Nine slides on positioning a staffing offer without saying "cheap"
#copywriting#carousels
GUARANTEE SPEED TO LEAD DONE-FOR-YOU 22 CALLS BOOKED EVERY WEEK CONTRACTOR OFFER STACK POST8.7K
Interstaff · Instagram
Contractor lead-gen: the offer stack that books 22 calls a week
#lead-gen#contractor

Links — Library. Covers, durations, engagement counts, creator handles and content type all arrive from the enrichment pass. Amber chips are what the AI proposed but I haven't accepted yet — suggestions stay visibly separate from tags I chose. Mockup data.

03 — Pillar one · Links

Save it in one keystroke, get it back with full context

A save is a URL and nothing else. Everything after that is the machine's job.

The pipeline resolves the source (Instagram, X, YouTube, LinkedIn, TikTok, generic web), then tries the cheapest extraction that could work and escalates only if it fails: oEmbed, then the platform's own API, then a rendered-HTML pass, then a paid scraper. Each field remembers which source produced it, so when a title comes back wrong I can see whether it came from OpenGraph or from a scraper, and fix the right layer.

What lands: title, creator and handle, original post date, cover image, full caption or article body, duration, engagement count, and a two-line summary. Then a classifier assigns project, category, subcategory and tags — scoped to the project, so the same tag carries a different meaning under Interstaff than under my personal brand.

Capture

Four ways in

Chrome extension overlay (⌘⇧S), iOS share sheet, Telegram message, or paste into the app. Offline saves queue and drain on reconnect.

Harvest

A page at a time

The extension can pull every outbound link on the page you're reading and save the set — useful for a roundup post or a competitor's resources page.

Watches

Creators, not just posts

Follow a creator and new posts arrive in the library on their own, skipping the inbox so they never bury the triage queue.

Reader

Notes on the artefact

Highlights, a rich-text note editor, and file uploads — photos, video, PDF, markdown — attached to the save itself.

instagram.com/reel/C8xK2p…
Save link⌘⇧S
instagram.com/reel/C8xK2pQr9Lm
Project
Interstaff High LevelPersonal
+ meta-ads+ hooks+ ad-creative
Harvest pageSave
Telegram · Link-o-Mator bot
/balance
Net worth · $18,412.60
BAC Colones₡1,284,000
BAC Dollars$6,940.12
Wink$3,110.00
Crypto$2,558.03
Debt−$6,477.55
automercado_receipt.jpg
Logged ₡38,450 at Automercado → Groceries › Supermarket, BAC Colones. 14 line items parsed.
⚠️ Eating out is at 136% of its $450 budget with 8 days left. Want me to move $150 from Software?

Left: the browser extension. A centred overlay on the page you're already reading, with the project pre-picked and tags pre-proposed. Right: the Telegram bot — the same database, reachable from a chat window. Photograph a receipt and it comes back parsed into line items. Two scheduled runs a day surface anything worth interrupting me for. Mockup data.

04 — Pillar two · Tasks

The task app I open because it costs nothing to feed

Task capture is a single text field. "call the dentist tomorrow 3pm" becomes a task titled Call the dentist, dated tomorrow at 15:00. "review the Q3 ad spend friday #interstaff" picks up the project and the tag. If parsing is ambiguous the raw text survives as the title, which is always better than losing it.

Then the ordinary things, done properly: Today, Upcoming, Week, Projects, Completed. Sub-task checklists. Recurrence. Swipe right on a phone to complete. Bulk edit from a selection bar. Reminders through native notifications on iOS and push on the web.

The part that only works because of the shared database: a task can point at a link. The competitor ad sits attached to the task about responding to it, and the task shows up in the project's view next to the money spent on that project.

Today THU 9 AUG
3 due · 1 overdue
Send the CAPI audit to Northgate Dental
Overdue 2d#interstaff
Record the voice-agent demo for the Tuesday post
3:00 PM#high-level
Reconcile July statement — 7 charges in Review
Money
Renew the Apify plan
Links
Tasks
Money
Search
Money JULY 2026
Net worth
$18,412.60
Cash$24,890
Debt−$6,478
Recent
AutomercadoGroceries › Supermarket · Aug 9 ₡38,450
AnthropicSoftware › AI tools · Aug 8 $120.00
To BAC DollarsTransfer · Aug 8 ₡250,000
Interstaff retainerIncome · Aug 5 +$4,200.00
Links
Tasks
Money
Search

The phone build. Same code as the web app, installed as a PWA or shipped through Xcode with Capacitor — including a share extension and a home-screen scan widget. Transfers render grey and sign-neutral so an internal move between my own accounts never reads as spending. Mockup data.

05 — Pillar three · Money

What you build when Plaid doesn't cover your country

This pillar took more work than the other two combined, for one reason: there is no bank API. Every consumer finance product in the US starts by connecting an account and receiving a clean transaction feed. I had to manufacture that feed from whatever my banks would give me.

So there are five doors in, and all five open onto the same corridor:

The corridor is the important part.

Every source produces the same draft shape and hands off to one pipeline. That pipeline dedupes bidirectionally and sign-aware, so re-importing a statement I already photographed doesn't create twins — and so a deduper never mistakes a transfer's opposite leg for a duplicate of itself. It pairs internal transfers by matching amount, direction and a tight time window, then marks both legs so they drop out of spending entirely. It matches charges against known subscriptions and debts by merchant alias, name or schedule. And it queues everything for the AI tagger.

Hard rule, written into the repo

I learned this the expensive way. Email import shipped first as its own parallel path — its own writer, its own dedupe, its own pairing logic. It produced duplicate transactions and transfers that matched across different days. I tore it out and rerouted it through the existing machine, then wrote the rule into CLAUDE.md so neither I nor the agent would do it again: any money feature routes through the existing transaction pipeline; never build a parallel path.

link-o-mator.app/money/review
Review
7 things the pipeline wants a second opinion on
Keep allApply 5
TRANSFER Two legs of one move between your accounts 98% match
Transferencia SINPE BAC Colones · Aug 8, 14:02 −₡250,000
PAIR ↔
Depósito recibido BAC Dollars · Aug 8, 14:04 +$486.20
Not a transferPair them
DUPLICATE Same charge arrived from two sources exact amount + date
Anthropic From: bank email · Aug 8 $120.00
KEEP ONE
ANTHROPIC PBC From: BAC statement.csv · Aug 8 $120.00
Keep bothMerge
SUGGESTED Categories the tagger proposed 4 charges
KOLBI RECARGAAug 7 · BAC Colones Utilities › Telecom ₡15,000
SERVICENTRO LA URUCAAug 6 · BAC Colones Car › Fuel ₡28,400
RESTAURANTE SIKWAAug 5 · BAC Dollars Eating out › Restaurants $78.40
Edit eachAccept all
RECURRING Charged 3 months straight, never registered $29.00/mo
FIGMA PROFESSIONAL hit on Jun 12, Jul 12 and Aug 12 for the same amount. It isn't in your subscriptions, so your fixed costs and 90-day outlook are both understating by $29/mo.
IgnoreAdd as subscription

Money — Review. The queue is the whole trick. Automation that silently guesses wrong is worse than no automation, so anything below confidence lands here with both candidates side by side and a one-click resolution. The badge in the sidebar is the only nag. Mockup data.

06 — How it got built

A Mac app that became a cloud app

The first version, in April, was a Tauri desktop app: Rust core, SQLite with full-text search, macOS Keychain for secrets, Notion as the source of truth. Fourteen commits, 107 Rust tests, and a working binary.

I killed it in June.

Not because it was bad — it was genuinely nice — but because it was wrong about my life. I save links from my phone more than my desk. A local SQLite file on a MacBook can't be the source of truth for someone whose capture surface is a phone in a supermarket queue. So the whole thing moved to a React PWA on InstantDB, deployed on Vercel, syncing everywhere in real time. Costa Rican internet made the offline-first sync model a requirement rather than a nicety.

That pivot cost about a week and bought back the entire product.

The stack, and why each piece is there
LayerChoiceReason
DataInstantDBReal-time sync, offline writes, one schema for all three pillars. Cheaper than Supabase at single-user scale.
AppReact 19 + TS strict + ViteOne codebase renders the web app, the PWA and the iOS build.
StyleTailwind v4 + CSS tokensEvery surface reads from variables, so light and dark are one palette swap.
Server12 Vercel functionsEnrichment, AI tagging, vision, email import, Telegram webhook, two daily crons. Twelve is the Hobby-plan ceiling, so it's a real constraint.
AIOpenRouterOne key, swappable models. Tagging, summarising and receipt vision all route through it.
CaptureWXT (MV3) + CapacitorChrome extension and native iOS from the same TypeScript.
ChatTelegram Bot API34 commands. The assistant surface I already had open all day.

I built it alongside an AI coding agent working from a written brief in the repo. The brief holds the schema, the taxonomy, the coding standards, the division of labour, and the hard rules — including the pipeline rule above. Every task ends with a typecheck, a test run and a conventional commit. That discipline is why 619 tests exist for a personal app: the agent writes them by default, and they're what let me refactor the money pipeline twice without losing a weekend.

07 — What broke

Three bugs worth the scar tissue

₡1,000 rendered as $1,000. The currency converter had a fallthrough where an unknown pair returned the amount untouched. On a screen mixing colones and dollars, that quietly displayed a ₡1,000 charge — about two dollars — as a thousand-dollar one, and my net worth was nonsense for a day. The fix was a rule the code now enforces: a cross-currency pair may never render 1:1. If there's no rate, fall back to a table; never pass the number through.

Transfers pairing across days. Bank emails carry a send time and a posting time, and I was matching on the wrong one. A colón transfer sent Friday night and posted Monday matched against an unrelated Monday deposit. Pairing now uses real send-time inside a tight window.

A build that passed locally and broke in production. Vercel type-checks the serverless functions with a stricter config than the app's, and the failure was silent. Every function now carries a directive on line one, and I verify production after every merge rather than trusting the deploy hook.

None of these are interesting engineering. All three are the kind of thing that only surfaces when you're the user, which is the actual argument for building your own tools.

08 — What changed

Outcomes, measured against the three graveyards

BeforeNow
Saving a link meant a browser tab, a Notion form and four fields I'd skip.⌘⇧S. Title, creator, summary, cover, category and tags arrive on their own.
1,400 saves, untagged and unsearchable.One search box over all three pillars, plus a tag hub that spans links, tasks and transactions.
Money visible once a month, if I sat down for an hour with a spreadsheet.Net worth, burn rate and a 90-day outlook, current to the last charge — reachable from a Telegram message.
Subscriptions I'd forgotten I was paying for.The pipeline flags any charge that repeats three months without being registered.
Reconciling two currencies by hand.Every account holds its native currency; the app converts for display and refuses to fake a rate it doesn't have.
A task app I didn't open.Tasks captured from the same box that captures everything else, and attached to the links and projects they came from.

The benefit I didn't design for, and now care about most: questions that span two pillars are answerable. How much did I spend on the tools I saved from that thread? What's still open on the project that's burning the most? Those were unanswerable when the data lived in five products, not because any product was weak, but because none of them could see the others.

09 — The uncomfortable part

Why this should worry a category of software

Nothing here is technically novel. Bookmark managers, task apps and budgeting tools have existed for twenty years, and the companies that make them employ people better at this than I am.

What changed is the cost of the bundle.

What it replacesTypical costWhy it couldn't serve me
Bookmark manager~$3/moNo AI enrichment for social posts; no idea what a project is.
Task manager~$5/moCan't attach a task to the artefact that produced it.
Budgeting app~$15/moRequires a bank connection that doesn't exist in my country.
Read-later + highlights~$8/moYet another silo with its own tags.
Automation glue~$20/moHeld the integrations together and broke whenever an API moved.
Link-o-Mator~$5/moHosting, plus metered AI and scraping. One schema, no glue.

Roughly $50 a month of subscriptions, none of which talked to each other, replaced by infrastructure I control — in three months of evenings, by one operator with an AI agent.

The moat was never the features. It was that assembling them used to take a team.

That's the disruption, and it isn't about me. Vertical SaaS has spent a decade selling generalised solutions to specialised problems, and charging for the generalisation. The Costa Rica gap is the clearest example: no budgeting company will ever build a BAC-email parser, because the market is too small. It took me an afternoon, because my market is one.

When bespoke becomes cheap, the products most exposed are the ones whose value is configuration — a form, a taxonomy, a set of rules a competent operator could specify in a paragraph. What survives is what's genuinely hard to reproduce: real network effects, real regulatory moats, real data nobody else has. A monthly fee for a nicely-shaped CRUD app around a schema is not that.

The second-order effect is stranger. Because I own the schema, every new idea is additive. Adding subscription detection didn't require a vendor to prioritise it, and it inherited every tag, project and account already in the system. Compounding like that is what a stack of SaaS subscriptions structurally cannot do, no matter how good each one is.

10 — Next

What's still on the board

It isn't a product for sale, and it may never be. It's the thing I use every day, which is a higher bar than most software I've paid for clears.


All screens above are faithful rebuilds of the shipped interface, populated with mockup data — no real financial figures appear anywhere on this page. Figures in the ledger are counted from the repository on 9 August 2026.

Link-o-Mator · 275 commits · 619 tests · Built in San José, Costa Rica