Skip to content
nsinaPublic

About

A self-hosted expense tracker for startups, solo founders and small agencies.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

49 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

koin-landing

Koin

Koin Logo

A self-hosted finance tracker for startups, solo founders, and small agencies.

MIT License Nuxt 4 D1 Cloudflare


What it does

Most finance trackers are either too expensive, too complicated, or want access to your bank account. Koin is a self-hosted Nuxt app that runs on your own Cloudflare account — or entirely locally if you prefer.

  • Mercury Bank Sync — Import via API (real-time) or CSV; auto-categorizes 100+ vendor patterns.
  • Expense Tracking — Log expenses with receipt attachments and IRS Schedule C category mapping.
  • Mileage Logging — Track business trips with current IRS standard rates.
  • Contractor Tracker — Monitor payments and automatically flag 1099-NEC ($600+) thresholds.
  • Client Revenue Forecasting — Track retainers, hourly clients, commitments, project dates, and monthly forecast rolloff.
  • Recurring Expenses — Auto-create monthly/quarterly/annual entries for SaaS and subscriptions.
  • Tax Reports — Generate CPA-ready deduction summaries by category with 1099-NEC prep.
  • IRS Calendar — Built-in reference for 1099-NEC, W-2, and Form 1040 deadlines.
  • Privacy First — Self-hosted on your own Cloudflare account. No 3rd party access to your data.
  • Backup & Restore — Export all data as JSON, import to any instance.

Tech Stack

  • Framework: Nuxt
  • UI: Nuxt UI + Tailwind CSS v4
  • DB: Cloudflare D1 and R2 for file storage | SQLite via Drizzle ORM for local development
  • Hosting: Cloudflare Workers (Optional)

Getting Started

Local Development

git clone https://github.com/nsina/koin.git
cd koin
pnpm install
pnpm dev

NuxtHub uses a local SQLite file (.data/hub/database) for development — no external database required. Pending migrations are applied automatically when the dev server starts; you can also run them explicitly with pnpm nuxt-db migrate. To reset your local database, delete .data/hub/database and restart.

This is the best way to test the full feature set, including Mercury API integration.

Mercury API Setup

  1. Generate a Read Only token at Mercury Settings → API Tokens
  2. In Koin, go to Settings and paste the token into the Mercury API field
  3. Use the Mercury Import tab to sync transactions by date range

The token is stored server-side in KV and never sent to the browser in plaintext.

Deployment

1. Configure Cloudflare Resources

Copy the example wrangler config and fill in your own Cloudflare resource IDs:

# 1. Copy and fill in your Cloudflare resource IDs
cp wrangler.example.jsonc wrangler.jsonc

Edit wrangler.jsonc with your D1 database ID, R2 bucket name, and KV namespace ID. You can find/create these in the Cloudflare dashboard:

  • D1: Storage & Databases → D1 SQL database → Create Database
  • R2: Storage & Databases → R2 Object Storage → Create bucket
  • KV: Storage & Databases → Workers KV → Create Instance

Keep the binding names DB, BLOB, and KV exactly as they are in the example — NuxtHub's drivers look them up by name.

2. Configure D1 migration credentials

wrangler deploy uploads the bundle but never applies D1 migrations. They run as an explicit step against the remote database over the D1 HTTP API, which needs three environment variables:

cp .env.production.example .env.production

Fill in your account ID, database ID, and a Cloudflare API token with the Account · D1 · Edit permission.

Keep these out of .env. Only pnpm db:migrate:prod reads .env.production. It explicitly selects the remote D1 driver and fails before deployment if any credential is missing; normal development always uses local SQLite.

3. Deploy to Cloudflare Workers

pnpm run deploy

That runs three steps in order — build with the Cloudflare preset, apply pending migrations to the remote D1, then upload:

pnpm build:cf         # NITRO_PRESET=cloudflare_module nuxt build
pnpm db:migrate:prod  # force remote D1, then nuxt-db migrate --dotenv .env.production
pnpm wrangler deploy

A bare pnpm build produces a node-server bundle with local filesystem drivers, which cannot run on Workers. Verify the deployed schema with:

pnpm nuxt-db sql "select * from _hub_migrations" --dotenv .env.production

Never run wrangler d1 migrations apply. It writes migration names into _hub_migrations in a different format than NuxtHub does, so neither runner recognizes the other's rows and the next deploy fails with table already exists. Use nuxt-db migrate only.

⚠️ Security

This app has no built-in authentication. If deployed to a public URL, protect it with Cloudflare Access before sharing:

  1. Go to Zero Trust → Access → Applications in the Cloudflare dashboard
  2. Add your Pages domain and configure an identity provider (Google, GitHub, email OTP)

Without Cloudflare Access, your expense data and bank transactions are publicly readable. For local use only, no additional auth is needed.

License

MIT — see LICENSE

About

A self-hosted expense tracker for startups, solo founders and small agencies.

Topics

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages