spl1t is an open source expense-tracking app based on Spliit. This fork deploys on Cloudflare Workers (via OpenNext) with Cloudflare KV as the database β not Vercel Postgres / Prisma.
Live: https://spl1t.pages.dev
spl1t.pages.devis a Pages project that reverse-proxies Workerspl1t. OpenNext still deploys the Worker. AppNEXT_PUBLIC_BASE_URLremainshttps://spl1t.USER.workers.devuntil a Worker rebuild.
Legend: π’ from original Spliit Β· π΄ new in this Cloudflare KV fork
- π’ Create a group and share it with friends
- π’ Create expenses with description
- π’ Display group balances
- π’ Create reimbursement expenses
- π’ Progressive Web App
- π’ Select all/no participant for expenses
- π’ Split expenses unevenly
- π’ Mark a group as favorite
- π’ Tell the application who you are when opening a group
- π’ Assign a category to expenses
- π’ Search for expenses in a group
- π’ Export a group to JSON or CSV
- π΄ Import a group from a Spliit JSON export (creates a new group with remapped IDs)
- π΄ Import a group from a Tricount GDPR CSV export (participants + expenses)
- π΄ Notes + activity history + document links in JSON export/import (
exportVersion: 3) - π΄ Copy an existing expense into a new draft
- π΄ Math expressions in the amount field
- π΄ Even-split cent remainder (no missing cents)
- π΄ Group default split mode
- π΄ Optional group PIN
- π΄ Share group via QR code
- π΄ Soft-delete / restore groups (30-day grace) + 24-month inactivity expiry
- π΄ Security headers, CSV formula escape, Zod input caps, expense date bounds
- π΄ Error boundaries + Drawer accessibility titles
- π΄ Paste-friendly amount parsing (US/EU grouped currency)
- π΄ Keyboard navigation restored in category/currency selectors
- π΄ Mobile group tab icons
- π΄ Stats: monthly spending (stacked category chart + breakdown) and balance timeline
- π΄ Optional calendar-month expense grouping
- π΄ Drag-reorder / Sort AβZ participants
- π΄ Multiple payers per expense (legacy single paidById migrated on read)
- π΄ Extra currencies: ARS, TRY, COP, JOD, MKD, MOP, MYR, VND
- β Upload and attach images to expenses (removed β see below)
- β Create expense by scanning a receipt (removed β see below)
- Next.js for the web application
- TailwindCSS for the styling
- shadcn/UI for the UI components
- Cloudflare KV for persistence (denormalized group documents)
- OpenNext Cloudflare + Workers for hosting
- Each group is stored as a single KV value under
group:{groupId}. - Categories are seeded under the
categorieskey. - Concurrent edits to the same group use last-write-wins (no Durable Objects / transactions).
- Friend-sized groups fit this model; very large groups may hit KV value size limits.
- Groups track
lastActivityAton expense create/update/delete and group settings updates. After 24 months without activity, cleanup soft-deletes them; soft-deleted groups can be restored for 30 days, then are hard-deleted. CallGET/POST /api/cron/cleanup-groupswithAuthorization: Bearer $CRON_SECRET(setCRON_SECRETin Worker env).
Ideas below track community demand from Spliit Cloudβs roadmap, upstream Spliit issues/PRs, and hardening patterns from anon-spliit (reimplemented for KV β not a code port of their E2EE/auth stack).
| Feature | Notes | Prior art |
|---|---|---|
| Copy expense | From the expense list or edit header icon (opens create prefilled). | Upstream #527; shipped in Spliit Cloud |
| Amount math | Expressions in the amount field (10+5.50, 5*8, β¦) on blur/save. |
Upstream #184; shipped in Spliit Cloud |
| Default split mode | Stored on the group (device localStorage can still override). | Upstream #366; shipped in Spliit Cloud |
| Even-split cents | Integer remainder allocation so balances donβt drop a cent. | Upstream #374 / #427; tracked by Spliit Cloud |
| Share QR | QR in the share popover. | Upstream #500; on Spliit Cloud roadmap |
| Optional group PIN | 4β8 digits; unlocks per browser session; hashed on the server, not returned to clients. | Upstream #373; on Spliit Cloud roadmap |
| Notes + history + document links in JSON | Export/import round-trips expense notes, group information, activity history, and document URLs (exportVersion: 3). |
Follow-up to upstream #546; expense notes also in #165 |
| Soft-delete + inactivity expiry | Manual soft-delete with 30-day restore; auto soft-delete after 24 months without activity; cron hard-deletes after grace. | Inspired by anon-spliit deletion/auto-delete work and upstream #420 |
| Paste amount parsing | Normalizes pasted US/EU currency amounts in number fields. | Upstream #531 |
| Selector keyboard nav | Category/currency pickers use cmdk CommandList. | Upstream #491 |
| Mobile tab icons | Icon-only tabs on small screens; labels from sm. | Upstream #539 |
| Monthly spending + balance timeline | CSS stacked category charts, category breakdown, and balance timeline on Stats. | Upstream #532 / #555 |
| Calendar month grouping | Optional group setting for roommate-style monthly lists. | Upstream #530 |
| Multiple payers | Split who paid an expense across several participants; balances/export/import aware. Legacy paidById migrates on read. | Upstream #396 |
| Reorder participants | Drag-and-drop + Sort AβZ; order persisted in KV. | Upstream #416 |
| Tricount import | GDPR CSV export via the same Import control as Spliit JSON. | Upstream #526 |
| Export / input hardening | CSV formula escape, Zod max caps, expense date bounds, security headers, error boundaries. | Patterns reviewed from anon-spliit (adapted for Workers/KV) |
On each groupβs Stats tab:
- Monthly spending β stacked category chart for calendar months, with a category breakdown and legend controls.
- Balance timeline β cumulative balances over time for participants (engineering fixes on this fork for share math / timeline consistency).
Inspired by upstream #532 / #555; reimplemented for denormalized KV documents.
On the Groups page, use Import JSON to upload:
- A Spliit JSON export (this fork or upstream Spliit), or
- A Tricount personal-data / GDPR CSV export (detected automatically).
Shared behavior:
- Always creates a new group (does not overwrite an existing one).
- Regenerates group, participant, and expense IDs so imports never collide with live data.
- Does not import a group PIN (PIN must be set again after import).
- Restores participants, expenses (including multiple payers when present), split modes, amounts, dates, notes, group information, and activity history (when present in the file).
- Categories: match by
idwhen present; otherwise byname/groupingagainst the seeded list (many exports omitid). - Newer exports include
exportVersion: 3, expenseids (needed to re-link history), and expense document links (url/ dimensions β not file bytes). - Document URLs round-trip for migration between Spliit forks; binaries are not embedded, and links may 404 if the original storage expires. Recurring-expense links are still not restored.
- Imports participants and expenses (amounts by share / impacted amounts).
- Uses the CSV default currency (Frankfurter
.devfor missing cross-rates). - Notes and activity history are Spliit-only; Tricount imports leave them empty.
- Prior art: upstream #526.
Upstream Spliit optional features that depended on AWS S3 and OpenAI are not available in this Cloudflare KV fork:
| Feature | Upstream dependency | Status here |
|---|---|---|
| Expense document / image uploads | S3 (or compatible object storage) | Removed from the critical path; UI/API stubs keep flags off. KV is not used for binaries. |
| Create expense from receipt scan | OpenAI + storage | Disabled; no OpenAI client or API keys. |
| Category extract from text/image | OpenAI | Disabled; same as above. |
What changed vs upstream:
- Prisma, Postgres, and Vercel-oriented DB wiring were replaced with the KV group-document API.
- S3/OpenAI packages and env vars were dropped; keep
NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS,NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT, andNEXT_PUBLIC_ENABLE_CATEGORY_EXTRACTunset orfalse(see.env.example). - Re-enabling uploads later would mean adding something like R2 (not stuffing files into KV). Receipt/category AI would need a Workers-compatible provider and explicit product work.
- Clone the repository:
git clone https://github.com/t0ma5/spl1t.git - Copy
.env.exampleto.envand.dev.varsas needed - Create a KV namespace and put its id in
wrangler.jsonc:
npx wrangler kv namespace create spl1t-db
npx wrangler kv namespace create spl1t-db --preview- Run
npm install(usespackage-lock.json) - Set
NEXT_PUBLIC_BASE_URL(production default inwrangler.jsoncvarsishttps://spl1t.pages.dev) - Run
npm run devfor Next.js local development (bindings via OpenNext), ornpm run previewto build and run in the Workers runtime
Note: Local OpenNext/Wrangler needs workerd, which does not support Windows ARM64. On those machines, develop against the remote Worker or deploy from an x64/Linux host.
Deploy directly to Cloudflare (no GitHub Actions).
Requires Node.js 22+ and a host where Wrangler/workerd runs (Linux / macOS / Windows x64 β not Windows ARM64).
npm run deployThis runs opennextjs-cloudflare build then deploys Worker spl1t. Ensure:
DBKV binding inwrangler.jsoncpoints at your namespace (existing id kept so group data survives).vars.NEXT_PUBLIC_BASE_URLmatches the URL users open (https://spl1t.pages.dev).
- Pushing code to GitHub does not update the live Worker until you run
npm run deploy(or equivalent OpenNext/Wrangler upload) against Cloudflare. - Prefer
git/ GitHub CLI over the GitHub web βupload filesβ UI β uploads often drop directories. - Set Worker secret
CRON_SECRETand schedule a daily call to/api/cron/cleanup-groupsfor inactivity cleanup.
GET /api/health/readinessorGET /api/healthβ app ready, including KV connectivityGET /api/health/livenessβ process alive only
- Original Spliit β idea, UI, and core expense-splitting product by Sebastien Castiel and contributors: spliit-app/spliit Β· spliit.app.
- Spliit Cloud (antonio-ivanovski/spliit-cloud) β community fork that continues Spliit with new features. Several UX improvements in this Workers/KV fork were prioritized from their roadmap and upstream issue links (reimplemented for denormalized KV documents, not a code port of their Postgres/API stack).
- anon-spliit (sora-grayscale) β privacy-focused fork (E2EE, private instance, deletion/auto-delete). This Workers/KV fork adapted selected lifecycle and hardening ideas from that work; it does not port their end-to-end encryption or account/2FA stack.
MIT, see LICENSE. Same license family as upstream Spliit and Spliit Cloud; retain their copyright notices where applicable.
