subs-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 1ae4feb2bebf
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 5af1a249bbbef1db1b4c5ec8
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
AGENTS.md
# Subs

Subs by OpenApps is a free subscription tracker and a free alternative to Rocket Money Premium, without a bank connection. It is an OpenApp in the OpenApps free apps store (https://openapps.free/), owned by the OpenApps team.

## What it promises

- Free, with no limit on subscriptions, no ads, no third-party trackers, nothing sold and no paid tier. The hosting counts visits (page views, a random visitor id, speed and errors). Never add a cap, an upsell or a tracker.
- No bank connection, ever. People type what they pay for. Say so plainly, including the trade: Subs cannot find a forgotten subscription.
- No sign-up to start. Every visitor is a guest account created in the browser; subscriptions are kept on the device and in that account. "Keep my subscriptions on all devices" signs in with Bounded hosted email login, and the guest's list merges into the account.
- Private: only the person can read their list (owner-only rules). No sharing, no API.
- Honest about reminders: a web page cannot reliably remind anyone without a server sending pushes, so Subs makes calendar events (.ics, or a Google Calendar link) and says so.
- It does not cancel anything. It links to each service's own website.
- Free alternatives are the heart of the app: each subscription is matched against the OpenApps store's catalog, which is how people find the rest of the store.
- Typical prices are only a starting point, always labeled "typical, check yours", and come from public pricing pages (see `src/lib/services.ts`).

## How it is built

- `index.html` is the static landing page that search engines and AI crawlers read. The quick add mounts into `#start`; the app's tabs mount into `#app-nav` on wide screens. Keep its copy, comparison table (Rocket Money figures from Rocket Money's own pricing page, with "Checked <Month YYYY>"), FAQ and JSON-LD (`FAQPage` must match the visible FAQ; bump `dateModified` when content changes) in step with what the app does. `public/index.md` is the same page as markdown for AI agents and `public/llms.txt` the guide for assistants; keep both true.
- `src/main.tsx` keeps `/` as the landing; `/home`, `/all`, `/add`, `/settings`, `/s/<id>` and `/s/<id>/edit` hide it and show the app (`src/Shell.tsx`). `/add?name=...&price=...&cycle=...&every=...&next=...&trial=1&category=...` opens the add form filled in (`draftFromQuery` in `src/Editor.tsx`); nothing is saved until the person taps Add.
- `src/lib/subs.ts` is the model and all the counting: billing cycles (day, week, month, year, every n), next charge dates (months keep their day, Jan 31 becomes Feb 28), per month and per year totals, by category, the next 30 days, free trials (a trial ends on `next`, the first charge), merging two copies, and CSV in and out. Amounts are integers in the minor unit of the person's one currency (`src/lib/money.ts`).
- `src/lib/store.ts` keeps the state in localStorage (`subs:v1`). Each subscription carries a timestamp, so two devices merge by the newer change; a deleted one stays as a marker with no details.
- `src/lib/sync.ts` syncs with Bounded: it reads the account's copy, merges, then writes only what changed, and reads again when the app comes back to the front (at most every 30 seconds). It never writes before the first successful read.
- `src/lib/services.ts` is the list of popular services for the name field's suggestions: website, category, other names, and some typical US prices with their cycle.
- `src/lib/alternatives.ts` matches a subscription to a store app by whole words of the paid app names in the catalog's `freeAlternativeTo` lists (longest name wins). `src/lib/catalog.json` is the shipped copy (refresh it with `npm run catalog`); the live list comes from the store's MCP server (`list_free_apps`) at most once a day per browser, because the store's static `catalog.json` sends no CORS header.
- `src/lib/ics.ts` builds the reminders: an all-day event a few days before each renewal (RRULE with the cycle; days past the 28th move to the 28th) or before a trial ends (one event), with a stable UID so a new download updates the events.
- `policy.json`:
  - `people/<userId>`: settings JSON (currency, reminder lead days). Owner only.
  - `people/<userId>/subs/<subId>`: one subscription as JSON. Owner only, no deletes (deleted ones are markers).
  - `policy-tests/personal.json` covers every rule: run `bounded tests run`.
- `src/sw.js` becomes `/sw.js` at build (`vite.config.ts` fills in the file list) and keeps the app on the device, so it opens offline. `public/manifest.webmanifest` and `public/icons/` (crops of the card art) make it installable.

## Working on it

`npm install && npm run build` typechecks and builds `dist/`. `npm run dev` serves it on port 4348. Ship changes through previews and releases as your runtime instructions describe; the app is an OpenApp, so nobody deploys it directly.

Check a change by adding a subscription from the landing hero, from a suggestion with the keyboard, one with a custom cycle and one free trial; checking the totals, the next 30 days and the free alternatives on the overview; downloading a reminder and opening it in a calendar; marking one barely used and one cancelled; exporting and importing a CSV; reloading; and opening the app at 390px wide.

## Voice

Plain words, short sentences, warm, no jargon, no em dashes. Say what happens, and say plainly what Subs does not do.

Change history

Point store links to openapps.free
1ae4feb2be · 18 hours ago
Bind agent address in policy
99bbab3f0c · 19 hours ago
Use OpenApps service URLs and add contribution strip
781c3c23e5 · 19 hours ago
Bounded source sync
048e3257a5 · 1 day ago
Bounded source sync
7a358a3be6 · 1 day ago
Bounded source sync
0c833fcb0e · 1 day ago