Open Net Worth
Open Net Worth is an open-source personal AI wealth assistant you run yourself: one ledger for your whole financial life, worked by an AI agent you control.
Facturas con los datos de la Resolución 55/2021, gratis, sin registro y sin internet. Imprime o guarda en PDF.
Translation pending
FinanceTypeScript · HTMLGitHub @athendatOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Generador de Código Abierto de facturas según la legislación vigente en Cuba
Translation pending
Loading the full README.
Read the full README ↗Free, open-source, offline-first invoice generator for Cuban businesses. No login and no invoice ever leaves the device: the app is a static site that runs entirely in the browser and keeps your data there. The only thing sent anywhere is a set of anonymous usage counters (see below), which the user can turn off.
npm start # dev server at http://localhost:4200
npm test # unit tests (Vitest through the Angular CLI)
npm run build # production build to dist/facturath/browser
npm run deploy # publish dist/facturath/browser to Cloudflare (needs wrangler login)
The production build emits plain static files with the single route prerendered at build time.
The site is served by Cloudflare Workers static assets: wrangler.jsonc
declares only the build output directory with single-page-application
routing, and public/_headers sets the cache policy (hashed bundles
immutable, HTML entry and service-worker manifest never cached).
GitHub Actions (.github/workflows/ci.yml) runs install, tests and the
production build on every pull request; the check fails on a failing
test, a size-budget error or any build warning. On push to main the
same workflow also deploys the build with wrangler, so the public URL
is always the latest green build of main.
The workflow needs two repository secrets:
| Secret | Value |
|---|---|
CLOUDFLARE_API_TOKEN | An API token created in the Cloudflare dashboard from the Edit Cloudflare Workers template (Workers Scripts: Edit on the account). |
CLOUDFLARE_ACCOUNT_ID | The account id shown in the Workers & Pages overview of the Cloudflare dashboard. |
The public URL is https://facturath.athendat.site. The first deploy
creates a Worker named facturath and attaches that custom domain
(routes in wrangler.jsonc), which needs the athendat.site zone on
the same Cloudflare account and the API token scoped to it. The
workers.dev and preview URLs are turned off so search engines only
see one copy of the site; a path that is not a file gets
public/404.html with a 404 status.
https://facturath.cu is served by the same Worker (a second custom
domain in routes). It is a different origin, so it starts with an
empty invoice list; .site keeps being served instead of redirecting,
because a redirect would leave the invoices already saved there out of
reach of their users.
https://facturath.athendat.cu answers a 301 to the same path and
query on .site. It is a separate Worker, facturath-cu-redirect, in
redirect-cu/: the app keeps invoices in browser storage, which is
scoped by origin, so serving it on both hostnames would give each user
two separate invoice lists. CI does not deploy it; after a change run
npx wrangler deploy -c redirect-cu/wrangler.jsonc.
To know how many devices use FACTURATH and how many invoices it issues, the app counts, per local day and with no identifier (#76):
| Event | Counted |
|---|---|
install | first open in a browser |
active-day | first open of the day |
active-month | first open of the month |
invoice | first print of each invoice (reprints not counted) |
save | each successful Guardar |
core/usage-stats.ts keeps the counts in localStorage (key
facturath.usage) until POST /api/usage accepts them, so offline use
is counted too. Only a verdict on the report itself (400, 413)
drops them; any other answer, a 403 from a firewall rule included,
keeps them for a later try within the 60-day window. The invoice ids that stop reprints from counting stay on
the device. The setting "Enviar estadísticas anónimas de uso" in Ajustes
turns it off; it starts off when the browser sends Global Privacy
Control. The rules both sides share live in domain/usage.ts.
The only server code is src/worker/: wrangler's run_worker_first
sends it /api/* alone, it checks the request is a small same-origin
POST, and it adds the counts to the counts table of the
facturath-stats D1 database: one row per day and event, never one per
device. It reads the client IP only as the in-memory key of its rate
limiter (see below) and never stores it, and Worker observability is off
so no request
log exists.
The tables are created by the migrations in migrations/:
npx wrangler d1 migrations apply facturath-stats --remote
To read the totals:
# Devices active per month and invoices issued per month
npx wrangler d1 execute facturath-stats --remote --command "SELECT substr(day, 1, 7) AS month, event, SUM(n) AS total FROM counts WHERE event IN ('active-month', 'invoice', 'install') GROUP BY month, event ORDER BY month, event"
# Daily active devices over the last 30 days
npx wrangler d1 execute facturath-stats --remote --command "SELECT day, n FROM counts WHERE event = 'active-day' AND day >= date('now', '-30 days') ORDER BY day"
Counts are per browser, not per person: one person on a phone and a computer counts twice, and clearing site data counts as a new install.
The endpoint is anonymous by design, so anyone can post made-up counts.
The table holds no personal data and has no read endpoint; the risk is
quota. On the Workers Free plan D1 stops every database of the
account once the account passes its daily row-write limit, until
midnight UTC. Three layers keep /api/usage from getting there (#78):
This is the beginning of the README. Read the rest at the original. Read the full README ↗