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.
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 b3b80918bc912dda8c53a48d
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.
AGENTS.md
# Invoice
Invoice is a free invoice and estimate maker, and a free alternative to FreshBooks, Invoice Simple and Zoho Invoice. It is an OpenApp in the OpenApps free apps store (https://free-apps.openapps.xyz/), owned by the OpenApps team.
## What it promises
- Free, with no limit on invoices, no watermark, no ads and no paid tier. Never add a cap, an upsell, a "made with" mark on the PDF, or tracking.
- No sign-up. Every visitor is a guest account created in the browser.
- The PDF is made in the browser. An invoice is stored only when its owner shares it by link.
- Anyone with an invoice's link can view it and download the PDF. Only someone holding its private (admin) key can edit it or mark it paid.
- Invoice does not collect payments or send email. It shows the payment instructions the owner writes (the first web address there becomes the client's Pay now button), and its Email client, Send reminder and Email receipt buttons are `mailto:` links that open the owner's own email app.
- AI assistants can do everything a person can through the public API (`functions/api.ts`), documented in `public/llms.txt` and `public/openapi.json`. Keep both true whenever the API changes, and keep `public/index.md` matching the landing page.
## How it is built
- `index.html` is the static landing page that search engines and AI crawlers read. Keep its copy, comparison, FAQ and JSON-LD in sync with what the app does.
- `src/` is the React app, styled with the shared kit (`public/kit.css`: dialogs, dropdowns, skeletons, empty states, motion tokens) plus `src/styles.css`. `/` mounts the editor (`NewInvoice`) and "Your invoices" (`Mine`: status, unpaid and overdue totals, CSV export) into the landing. `/i/<id>` is the client's view; `/i/<id>/admin#k=<key>` is the owner's view (mark paid or unpaid, email, remind or send a receipt, edit, Duplicate; Make invoice on an estimate). Both hide the landing. The invoice page and the Bounded client load only when needed (`src/lib/bounded.ts` imports the client on first use), so the landing ships React and the editor only.
- An estimate is an invoice with `kind: "estimate"` in its data: it prints as ESTIMATE with "Valid until", is never paid or overdue, and has its own number sequence. Absent `kind` means an invoice.
- `functions/lib/invoice.ts` holds the totals math for both the app and the API: amounts are integers in the currency's minor unit, quantities keep 4 decimals and rates 3, products are exact (BigInt) and rounded half up once per line, once for the discount and once per tax rate. The discount comes off before tax and is shared across lines in proportion to their amounts, so each rate is charged on its discounted share.
- `src/pdf.ts` draws the PDF with pdf-lib and the standard Helvetica fonts (multi-page, the table header repeats). Those fonts cover Western European text only; other letters lose their accents or print as "?".
- `src/Paper.tsx` is the on-screen paper, laid out like the PDF and scaled to its column; below 600px it reflows at full text size instead of shrinking (a scaled page is unreadable on a phone).
- `src/store.ts` is what stays on the device (localStorage): business details, last currency, tax rate and payment instructions, the next invoice and estimate numbers, the current draft, clients and items billed before (offered as you type), and the invoices shared from this device with their private keys. Duplicate and Make invoice write a copy as the draft and open the editor.
- `functions/lib/config.ts` holds the app id and the site URL used in links the API hands back.
- `policy.json` holds the data rules (the link is the permission to read; an admin key makes an owner):
- `invoices/<id>` {data: JSON string of every field, logo as a small data URL; total; currency; status; paidAt}: read needs a join, update needs an owner, data is capped at 200,000 characters.
- `invoices/<id>/joins/<user>`: anyone who knows the id can join.
- `invoices/<id>/owners/<user>`: the creator in the creating batch, or anyone whose `invoiceclaims/<id>/c/<user>` key matches `invoicekeys/<id>`.
- `invoicekeys/<id>`: set once at creation, readable only by the public API principal, which checks the `x-owner-key` header before any owner action.
- The keys rule mentions `@const.BOUNDED_PUBLIC_PRINCIPAL_API`, which exists only once the app exists: a brand new app first deploys with `invoicekeys` read set to `"false"`.
## Working on it
```bash
npm install
npm run build # typecheck + build to dist/
```
Invoice is an OpenApp, so nobody deploys it directly: changes ship through previews and releases as your runtime instructions describe.
Check a change by filling an invoice with odd quantities, a tax rate and a discount, downloading the PDF and reading it, sharing it, opening the client link in another browser, marking it paid from the private link in a third, and calling the API with curl (including a wrong owner key). Check phones at 390px wide: the paper must stay readable and every button reachable.
## Voice
Plain words, short sentences, no jargon, no em dashes. Say what happens, not how clever it is.
Change history
Quality pass: phone-readable invoice, estimates, email client, saved clients, CSV export, openapi.json
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync