Skip to content

Repository files navigation

Ledger

A subscription command center for people whose software spend has quietly gotten away from them. Built from the Ledger.dc.html Claude Design canvas — same layout, same tokens, same interactions — then extended into a working app with eleven screens, ten demo personas, and a real new-user path.

npm install
npm run dev      # http://localhost:5178

Signing in

Judges — use the demo account:

Username judge
Password ledger2026

Both are printed on the login page with a Fill it in for me button. It's seeded into the database on first run and loads all ten worked personas.

Or create an account. Create account on the login page makes a real user record, gives you your own empty workspace, and drops you at onboarding with a CSV upload waiting. Accounts are isolated — one user cannot see another's workspaces. Sign out and delete-account live in the sidebar and on Plans & billing.

Passwords are salted and hashed with PBKDF2-SHA256, 150,000 iterations, via the Web Crypto API. The plaintext is never stored. But be clear about the limit: with no server this is a demo door, not real security. Anyone with access to the browser profile can read the digest and run offline guesses, and there is no rate limiting, no email verification and no password reset. Real auth needs a backend.

The built-in database

There's no server, so Ledger ships its own database: IndexedDB, the transactional, indexed store built into every browser. Five object stores, a versioned schema and owner indexes, all in src/lib/db.ts:

Store Key Holds
users username Account records — display name, email, salt, PBKDF2 digest
workspaces id (index: owner) Workspaces people create
mutations owner::workspaceId (index: owner) Every change to a workspace — edits, cancellations, decisions, imports
prefs owner Theme and the workspace you were last in
imports id (index: owner) CSV import history: file, row counts, timestamp

The ten demo personas stay compiled into the bundle as read-only seed data — your changes to them are stored as a mutation layer on top, which is why Reset this workspace can undo everything without re-downloading anything. State from the earlier localStorage build migrates into the database automatically on first load.

Everything is still local to one browser. No sync, no sharing between devices, and clearing site data clears the database.

Importing a CSV

Import CSV on the Subscriptions screen, or Upload a CSV in onboarding.

  • Drag a file in or pick one — it's parsed in the browser and never uploaded.
  • Columns are matched by name (vendor, price, billing cycle, renewal, last used, active days, payment method, notes) and you can correct any of them before anything is imported.
  • Dates or day counts both work: 2026-09-08, 19, in 19 days, today.
  • A preview shows every row with the price, cycle, renewal and the verdict Ledger derived, plus per-row warnings for anything it had to guess.
  • There's a downloadable template and a Try the sample button with eight realistic rows.

The parser is a proper RFC4180-ish implementation — quoted fields, escaped quotes, embedded newlines, CRLF and BOM all handled (src/lib/csv.ts).

The screens

Every item in the left nav is live. Nothing is a placeholder.

Screen What it does
Command Center Monthly recurring with the month-over-month story, a stat rail, the Decide today queue, a 60-day renewal runway with hoverable ticks and deal-revert diamonds, spend-by-category, longest-unused vendors, and department seat utilization on teams.
Subscriptions Five tabs (Active, Trials, Metered & API, Deals & credits, Archive), ledger and card views, saved-view chips, search, and multi-select bulk actions.
Expenses One-off charges beside the ledger — filter by type and category, add, delete, export. Business and reimbursable totals tracked separately.
Calendar A month grid of every dated money event: renewals, annual renewals, trial conversions, deal reverts. Click a day to see exactly what hits the card.
Usage Every tool ranked by usage score, with signal confidence, active days, cost per active day and a 12-week sparkline. Sortable four ways.
Savings The recommendation queue, derived from your own ledger. Acting on one actually performs it; open / actioned / dismissed are all tracked.
My AI Stack Your AI tools grouped by the job they do, with overlap detection and a Markdown export.
Workspace Teams only: people and seats, the approval queue with conflict context, shadow-IT discovery, and automation policies.
Reports Month-over-month totals, biggest movers, category breakdown, and CSV / Markdown / print export.
Integrations Connect and disconnect cost sources, usage signals and alert channels. Coverage is scored honestly.
Plans & billing Usage against Free limits, invoices, the four-tier comparison, and a cancel flow that doesn't fight you.

Plus three overlays: the subscription detail drawer (verdict and why, cost per active day, wasted-since-use, 90-day usage timeline, deal countdown, overlap consolidation, seat roster, billing history), the upgrade sheet, and a ⌘K command palette.

Ten demo personas

Each is a full workspace — its own ledger, expenses, people, approvals, discovery queue, integrations and category history.

Persona Shape What it exercises
Maya Alvarez Solo founder · USD Deal reverts, trials, zombies, tool overlap, metered burn, prepaid credits
Dana Whitfield Freelance designer · USD A price that doubled, a shipped project's leftover tooling, lifetime deals
Tomás Rivera Creator studio · USD Three clipping tools, two analytics tools, a deal reverting this week
Kavya Sharma Solo marketer · INR Client tools on a personal card, two SEO suites, a silent autopay renewal
Arjun Menon Indie developer · INR True-cost forex, UPI e-mandate health, local-pricing arbitrage
Priya Raghavan CS student · INR An expired student pack, two AI assistants, a course at 22%
The Osei Household Family · USD Shared seats, four profiles, two streaming services nobody opens
Northwind AI Startup team · 24 Seat reclaim, a pilot that never rolled out, approvals
Ridgeline Ventures Small fund · 9 Three sourcing tools, $600/mo of idle research seats
Helix Labs Scale-up · 41 $39K of inference, 19 idle Copilot seats, a four-deep approval queue

Switch between them in the workspace menu, or with ⌘K.

Starting fresh

+ Start a new workspace in the workspace menu creates a genuinely empty one. It gets its own onboarding: add what you already know, connect a cost source, or log a one-off expense. Screens that need data show that path instead of empty charts, and the Command Center drops the month-over-month badge until there is a second month to compare.

Everything a new workspace does is the same code the personas run — adding a subscription scores it, gives it a verdict, puts it on the calendar, and starts generating recommendations immediately.

What's actually wired

  • Add, edit and delete subscriptions. New rows get a usage score and verdict from the numbers you enter (src/lib/verdict.ts); Ledger writes the reason line if you leave the note blank.
  • Recommendations are derived, not scripted (src/lib/recommend.ts) — from verdicts, deal reverts, trials, idle seats, price hikes, capability overlaps, local-pricing gaps and duplicate coverage. Which is why they work for a workspace created five minutes ago.
  • Acting on a recommendation performs it: cancellations open a task, seat reclaims release seats and shrink the paid count, consolidations open the keeper. Decisions persist with a snapshot, so the Actioned list survives the recommendation disappearing from the queue.
  • Mark used rewrites a row's signals — confidence drops to self-reported, and a Cancel/Zombie verdict softens to Watch.
  • Approvals, discovery and policies are real state: approve, decline, reverse a decision, claim or flag a tool, toggle a policy.
  • Integrations connect and disconnect, and coverage feeds the Usage screen's honesty about whether a verdict is evidence or a guess.
  • Exports are real downloads — subscriptions CSV, expenses CSV, a Markdown report, and your AI stack as Markdown.
  • Everything persists per account, per workspace, in the IndexedDB database.
  • CSV import parses in-browser, maps columns, previews with derived verdicts, and records the import in the database.
  • Keyboard: ⌘K palette, g then c / s / e / u / v / r / i / p to jump, esc to close the top layer.
  • Repeatable demos: ⌘KReset this workspace to its demo state undoes every change in the current workspace, so a recording can be retaken without reloading. Workspaces you created can be deleted from the workspace switcher.

Layout

src/
  data/       seed.ts       the three design personas, verbatim
              personas.ts   seven more, hand-authored
              extras.ts     expenses, people, approvals, discovery, integrations
              workspaces.ts composition + the blank new workspace
              types.ts
  lib/        db.ts         IndexedDB schema, transactions, owner queries
              crypto.ts     PBKDF2-SHA256 password hashing
              csv.ts        CSV parser, column detection, row → subscription
              format.ts     money, dates, seeded sparklines
              derive.ts     row mapping, runway, verdict/confidence maps, CSV out
              recommend.ts  the savings engine
              verdict.ts    scoring rules for manually-entered rows
  state/      auth.tsx      accounts, sign in/up/out, session
              store.tsx     context store, database-backed per account
  components/ eleven screens, four overlays, three modals
  styles/     tokens.css (dark + light), app.css

The demo clock is pinned to 20 Aug 2026 (TODAY in src/lib/format.ts) so every "renews in 12d" lines up with the design's copy. Point it at new Date() when real data lands.

Assumptions worth knowing

  • Two base currencies. USD and INR, matching the design's forex model (₹88.50 plus a 3.5% markup). Adding more means a rate table, not new UI.
  • Seat prices are per seat, per period. Flat platform fees carry no seat count, so idle-seat maths stays honest.
  • Downgrade savings are estimated at 40% of the current price, and labelled with a — the app doesn't know the vendor's tier below.
  • Vendor hand-offs stop at the boundary. Plan changes, payment fixes and cancellation pages surface an explanatory toast where a real integration would take over; Ledger never claims to have cancelled something on your behalf.
  • The database is per-browser. IndexedDB is a real database with real transactions, but it is not a server — no sync across devices, no shared team workspaces, and no recovery if the profile is wiped.
  • Imported rows carry inferred confidence. A CSV tells Ledger what you pay, not what you use, so imports never claim a verified usage signal.

About

My ledger

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages