Envelope budgeting in one HTML file, with a forecast of what you can safely spend. Local-first: your budget is one JSON file on your own machine.
0
stars
38
commits
Python
primary language
Sep 11, 2026
updated
Envelope budgeting in one HTML file, with a forecast that tells you what you can safely spend. Your budget lives in a single JSON file on your own machine. No account, no cloud, no subscription, no build step, no npm.

finance-data.json next to the server. Back it up, git it, open it in an editor. When the project stops existing, your data does not.Who it is not for. There is no bank sync: you record transactions by hand or import a CSV statement. There is no multi-user permission model and no authentication; it is a household tool, not a service. And it is a single maintainer's opinionated budget app: features are added when the maintainer's own budget needs them.
Start the server (next section) and open http://localhost:8765/?demo=1. That loads a synthetic dataset and never writes anything to disk.
pip install.
winget install Python.Python.3.12, or install Python 3.12 from the Microsoft Store.brew install python, or the installer from python.org.sudo apt install python3 or your distribution's equivalent.color-mix() and ES2020). Phones and tablets included.Windows, the easy way: the installer. Download PocketEnvelopes-Setup-<version>.exe from the latest release and run it. It needs no Python and no administrator rights: it bundles the official Python runtime, installs for your user only (under %LOCALAPPDATA%\Programs\Pocket Envelopes), and puts Pocket Envelopes in the Start menu. That shortcut starts the server if it is not already running and opens the app. Your budget is kept in %LOCALAPPDATA%\PocketEnvelopes\finance-data.json, which the uninstaller deliberately leaves in place. Tick Start the server when I sign in during setup if you use the app from a phone or as an installed app and want it always reachable.
Windows SmartScreen will warn about an unrecognised publisher the first time, because the installer is not code-signed. Choose More info → Run anyway, or verify the SHA-256 printed on the release page first. You can also build the installer yourself with
installer\build.ps1(seeinstaller/).
Windows, from the source folder
launch.bat
Double-clicking it works too. Uses the Python already on your machine; if it is missing it prints install instructions and waits instead of failing silently.
macOS / Linux
./launch.sh
Anywhere, by hand
python3 serve.py
All three start the server on http://localhost:8765/, bound to this machine only, and open the app. The server shuts itself down after 30 minutes with no requests; set IDLE_TIMEOUT=0 to keep it running, PORT=… to move it. Press Ctrl-C to stop it early.
On first run, click Create new budget. That writes finance-data.json into the folder; every later visit loads and saves it automatically.
Why a server for a single HTML file? The server is the storage layer. It owns the JSON, hands it to whichever device asks, and writes it back atomically. Opening the app straight from the file system would leave it with nowhere to save. The file is named
pocket-envelopes.apprather than.htmlprecisely so a double-click cannot open it that way by accident.
The Dashboard leads with Spendable today (recorded cash less positive envelope reservations) and Lowest projected spendable over the exact Forecast horizon. The projection includes due entries still to be recorded; its assumptions state whether envelope allowances are included. Account selection and allowances carry over from Forecast, while chart-line visibility does not hide the dashboard's spendable summary.
On phones, the bottom navigation opens the four main views; More holds Accounts, Recurring, Net Worth, Reports, Settings and Help. Forecast puts its horizon above the chart, with collapsible settings. Transactions keeps search visible and tucks secondary filters behind Filters. The transaction dialog accepts arithmetic, keeps validation beside the field, and expands optional notes when needed. Envelope cards label available funds separately from this month's spending; Today marks the calendar pace on each budget bar.
The in-app Help tab explains the concepts (accounts vs envelopes vs spendable cash, allowances, "lowest in period") and answers the questions that come up in practice.
2380,50-100), keyboard shortcuts, dark / light / auto theme.Your budget is finance-data.json, next to serve.py. The app reads it with GET /data when the page loads and writes it back with PUT /data about a second after you stop editing, and immediately when you close or hide the tab.
fsyncs it, then renames it over the real one. A crash mid-save cannot leave a half-written file.finance-data.bak.0 (newest) through finance-data.bak.4.To restore from a disk backup: stop the server, copy finance-data.bak.N over finance-data.json, start it again. From a browser backup: Settings → Backups → Restore; a wrong restore is one Ctrl-Z away.
If two devices edit at once, the second save gets a 409 and a dialog: reload to take the newer version, or overwrite with the tab you are in. Unsaved work stays in the tab either way, and a forced overwrite still rotates the replaced version into .bak.0.
With the Windows installer, run the newer setup over the old one: it stops the running server, replaces the program files and leaves your data where it is. From a source folder, replace the files with the new release (or git pull), restart the server, and reload the app. Your finance-data.json is never part of the repository. The app migrates older data files forward automatically; CHANGELOG.md marks the changes that affect how money is counted, so you know when to glance at your figures. If you installed it as an app, a reload picks up the new shell.
Installed with the Windows installer: Settings → Apps → Pocket Envelopes → Uninstall. It stops the server, removes the program and shortcuts, and keeps your budget at %LOCALAPPDATA%\PocketEnvelopes; delete that folder yourself if you no longer want it.
From a source folder: stop the server (Ctrl-C, or end the scheduled task or service if you set one up), delete the folder, and keep or delete finance-data.json as you prefer. If you used tailscale serve, run tailscale serve reset. Remove the installed app from your home screen or browser like any other.
There is no authentication. Whoever can reach the server's port can read and overwrite your budget. So:
127.0.0.1 by default and the launchers keep it there. Setting BIND=0.0.0.0 exposes the unauthenticated API to every device on your network; only do it on a network you trust completely.tailscale funnel on it.SECURITY.md has the full threat model and how to report a problem privately.
Install Tailscale on the machine running the server and on each device you want to budget from, sign them into the same tailnet, then on the server machine:
tailscale serve --bg 8765
The app is now at https://<machine-name>.<tailnet>.ts.net/: a real HTTPS origin with a valid certificate, reachable only from your own devices, while the server itself stays on loopback. Every device reads and writes the one finance-data.json. tailscale serve reset undoes it.
The server only answers requests addressed to this machine: localhost, a literal IP address, its own hostname, any *.ts.net name, or the names you list in ALLOWED_HOSTS (comma-separated, no port). If you put a different reverse proxy in front with its own hostname, start the server with ALLOWED_HOSTS=budget.home.lan. Anything else gets a 421 — that is what keeps a malicious web page from reaching the loopback port through DNS rebinding; it is not authentication.
Keeping it running. Set IDLE_TIMEOUT=0 and start the server at boot:
serve-daemon.cmd runs it windowless with the right settings. Register it from an elevated terminal:
schtasks /create /tn PocketEnvelopesServer /tr "C:\path\to\serve-daemon.cmd" /sc onstart /ru SYSTEM /rl HIGHEST
A boot-time task has to run as SYSTEM (no other account is available before anyone signs in). The data file then gets rewritten by SYSTEM, which is fine: it inherits the folder's permissions, so your own account keeps full access.launchd daemon running IDLE_TIMEOUT=0 BIND=127.0.0.1 python3 serve.py from the project folder.Over the HTTPS origin above (or on localhost), the app is installable as a PWA:
A service worker caches the app shell, so it opens even when the server is asleep and tells you it cannot reach it, instead of showing a browser error. Your data is deliberately never cached: opens offline, edits online.
PORT=8790.tailscale serve) the proxy is up and the backend is not. Start the server again and reload.en-US, de-DE, en-GB).pocket-envelopes.app is one HTML file: CSS, then a single script that holds the data model, the accounting helpers, the forecast, and one render loop per tab. serve.py is a SimpleHTTPRequestHandler with two extra routes, GET and PUT /data, an ETag per response, and an idle watchdog. sw.js caches the shell network-first and never touches /data. The developer guide, including the decision log that explains every accounting rule and why it is the way it is, is CLAUDE.md; node audit/run-audit.mjs syntax-checks the app and unit-tests its accounting helpers.
The Windows installer is not code-signed, so SmartScreen shows an "unrecognised publisher" warning on first run. Verify it instead: every release installer is built by the Windows installer workflow on GitHub Actions from the tagged commit, never on a personal machine, and the release page prints its SHA-256 next to the download. Compare that hash with Get-FileHash PocketEnvelopes-Setup-<version>.exe in PowerShell, or build the installer yourself from installeruild.ps1 and compare the two. Signing is on the list for when the project has a track record; nothing about the app changes when it arrives.
Privacy policy: this program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. The app makes no outbound network calls of its own; the only network activity is between your browser and the server you run.
See CONTRIBUTING.md for setup and checks, and ROADMAP.md for the path to 1.0 and useful first contributions. Bug reports with a reproduction on the demo data are the most useful thing you can send.
MIT — see LICENSE. Chart.js is © Chart.js Contributors, MIT, vendored under vendor/ with its license text.
Python
51.5%
JavaScript
35.8%
Inno Setup
4.2%
Batchfile
3.6%
Shell
2.5%
PowerShell
2.3%
Envelope budgeting in one HTML file, with a forecast of what you can safely spend. Local-first: your budget is one JSON file on your own machine.
0
stars
38
commits
Python
primary language
Sep 11, 2026
updated
Envelope budgeting in one HTML file, with a forecast that tells you what you can safely spend. Your budget lives in a single JSON file on your own machine. No account, no cloud, no subscription, no build step, no npm.

finance-data.json next to the server. Back it up, git it, open it in an editor. When the project stops existing, your data does not.Who it is not for. There is no bank sync: you record transactions by hand or import a CSV statement. There is no multi-user permission model and no authentication; it is a household tool, not a service. And it is a single maintainer's opinionated budget app: features are added when the maintainer's own budget needs them.
Start the server (next section) and open http://localhost:8765/?demo=1. That loads a synthetic dataset and never writes anything to disk.
pip install.
winget install Python.Python.3.12, or install Python 3.12 from the Microsoft Store.brew install python, or the installer from python.org.sudo apt install python3 or your distribution's equivalent.color-mix() and ES2020). Phones and tablets included.Windows, the easy way: the installer. Download PocketEnvelopes-Setup-<version>.exe from the latest release and run it. It needs no Python and no administrator rights: it bundles the official Python runtime, installs for your user only (under %LOCALAPPDATA%\Programs\Pocket Envelopes), and puts Pocket Envelopes in the Start menu. That shortcut starts the server if it is not already running and opens the app. Your budget is kept in %LOCALAPPDATA%\PocketEnvelopes\finance-data.json, which the uninstaller deliberately leaves in place. Tick Start the server when I sign in during setup if you use the app from a phone or as an installed app and want it always reachable.
Windows SmartScreen will warn about an unrecognised publisher the first time, because the installer is not code-signed. Choose More info → Run anyway, or verify the SHA-256 printed on the release page first. You can also build the installer yourself with
installer\build.ps1(seeinstaller/).
Windows, from the source folder
launch.bat
Double-clicking it works too. Uses the Python already on your machine; if it is missing it prints install instructions and waits instead of failing silently.
macOS / Linux
./launch.sh
Anywhere, by hand
python3 serve.py
All three start the server on http://localhost:8765/, bound to this machine only, and open the app. The server shuts itself down after 30 minutes with no requests; set IDLE_TIMEOUT=0 to keep it running, PORT=… to move it. Press Ctrl-C to stop it early.
On first run, click Create new budget. That writes finance-data.json into the folder; every later visit loads and saves it automatically.
Why a server for a single HTML file? The server is the storage layer. It owns the JSON, hands it to whichever device asks, and writes it back atomically. Opening the app straight from the file system would leave it with nowhere to save. The file is named
pocket-envelopes.apprather than.htmlprecisely so a double-click cannot open it that way by accident.
The Dashboard leads with Spendable today (recorded cash less positive envelope reservations) and Lowest projected spendable over the exact Forecast horizon. The projection includes due entries still to be recorded; its assumptions state whether envelope allowances are included. Account selection and allowances carry over from Forecast, while chart-line visibility does not hide the dashboard's spendable summary.
On phones, the bottom navigation opens the four main views; More holds Accounts, Recurring, Net Worth, Reports, Settings and Help. Forecast puts its horizon above the chart, with collapsible settings. Transactions keeps search visible and tucks secondary filters behind Filters. The transaction dialog accepts arithmetic, keeps validation beside the field, and expands optional notes when needed. Envelope cards label available funds separately from this month's spending; Today marks the calendar pace on each budget bar.
The in-app Help tab explains the concepts (accounts vs envelopes vs spendable cash, allowances, "lowest in period") and answers the questions that come up in practice.
2380,50-100), keyboard shortcuts, dark / light / auto theme.Your budget is finance-data.json, next to serve.py. The app reads it with GET /data when the page loads and writes it back with PUT /data about a second after you stop editing, and immediately when you close or hide the tab.
fsyncs it, then renames it over the real one. A crash mid-save cannot leave a half-written file.finance-data.bak.0 (newest) through finance-data.bak.4.To restore from a disk backup: stop the server, copy finance-data.bak.N over finance-data.json, start it again. From a browser backup: Settings → Backups → Restore; a wrong restore is one Ctrl-Z away.
If two devices edit at once, the second save gets a 409 and a dialog: reload to take the newer version, or overwrite with the tab you are in. Unsaved work stays in the tab either way, and a forced overwrite still rotates the replaced version into .bak.0.
With the Windows installer, run the newer setup over the old one: it stops the running server, replaces the program files and leaves your data where it is. From a source folder, replace the files with the new release (or git pull), restart the server, and reload the app. Your finance-data.json is never part of the repository. The app migrates older data files forward automatically; CHANGELOG.md marks the changes that affect how money is counted, so you know when to glance at your figures. If you installed it as an app, a reload picks up the new shell.
Installed with the Windows installer: Settings → Apps → Pocket Envelopes → Uninstall. It stops the server, removes the program and shortcuts, and keeps your budget at %LOCALAPPDATA%\PocketEnvelopes; delete that folder yourself if you no longer want it.
From a source folder: stop the server (Ctrl-C, or end the scheduled task or service if you set one up), delete the folder, and keep or delete finance-data.json as you prefer. If you used tailscale serve, run tailscale serve reset. Remove the installed app from your home screen or browser like any other.
There is no authentication. Whoever can reach the server's port can read and overwrite your budget. So:
127.0.0.1 by default and the launchers keep it there. Setting BIND=0.0.0.0 exposes the unauthenticated API to every device on your network; only do it on a network you trust completely.tailscale funnel on it.SECURITY.md has the full threat model and how to report a problem privately.
Install Tailscale on the machine running the server and on each device you want to budget from, sign them into the same tailnet, then on the server machine:
tailscale serve --bg 8765
The app is now at https://<machine-name>.<tailnet>.ts.net/: a real HTTPS origin with a valid certificate, reachable only from your own devices, while the server itself stays on loopback. Every device reads and writes the one finance-data.json. tailscale serve reset undoes it.
The server only answers requests addressed to this machine: localhost, a literal IP address, its own hostname, any *.ts.net name, or the names you list in ALLOWED_HOSTS (comma-separated, no port). If you put a different reverse proxy in front with its own hostname, start the server with ALLOWED_HOSTS=budget.home.lan. Anything else gets a 421 — that is what keeps a malicious web page from reaching the loopback port through DNS rebinding; it is not authentication.
Keeping it running. Set IDLE_TIMEOUT=0 and start the server at boot:
serve-daemon.cmd runs it windowless with the right settings. Register it from an elevated terminal:
schtasks /create /tn PocketEnvelopesServer /tr "C:\path\to\serve-daemon.cmd" /sc onstart /ru SYSTEM /rl HIGHEST
A boot-time task has to run as SYSTEM (no other account is available before anyone signs in). The data file then gets rewritten by SYSTEM, which is fine: it inherits the folder's permissions, so your own account keeps full access.launchd daemon running IDLE_TIMEOUT=0 BIND=127.0.0.1 python3 serve.py from the project folder.Over the HTTPS origin above (or on localhost), the app is installable as a PWA:
A service worker caches the app shell, so it opens even when the server is asleep and tells you it cannot reach it, instead of showing a browser error. Your data is deliberately never cached: opens offline, edits online.
PORT=8790.tailscale serve) the proxy is up and the backend is not. Start the server again and reload.en-US, de-DE, en-GB).pocket-envelopes.app is one HTML file: CSS, then a single script that holds the data model, the accounting helpers, the forecast, and one render loop per tab. serve.py is a SimpleHTTPRequestHandler with two extra routes, GET and PUT /data, an ETag per response, and an idle watchdog. sw.js caches the shell network-first and never touches /data. The developer guide, including the decision log that explains every accounting rule and why it is the way it is, is CLAUDE.md; node audit/run-audit.mjs syntax-checks the app and unit-tests its accounting helpers.
The Windows installer is not code-signed, so SmartScreen shows an "unrecognised publisher" warning on first run. Verify it instead: every release installer is built by the Windows installer workflow on GitHub Actions from the tagged commit, never on a personal machine, and the release page prints its SHA-256 next to the download. Compare that hash with Get-FileHash PocketEnvelopes-Setup-<version>.exe in PowerShell, or build the installer yourself from installeruild.ps1 and compare the two. Signing is on the list for when the project has a track record; nothing about the app changes when it arrives.
Privacy policy: this program will not transfer any information to other networked systems unless specifically requested by the user or the person installing or operating it. The app makes no outbound network calls of its own; the only network activity is between your browser and the server you run.
See CONTRIBUTING.md for setup and checks, and ROADMAP.md for the path to 1.0 and useful first contributions. Bug reports with a reproduction on the demo data are the most useful thing you can send.
MIT — see LICENSE. Chart.js is © Chart.js Contributors, MIT, vendored under vendor/ with its license text.
Python
51.5%
JavaScript
35.8%
Inno Setup
4.2%
Batchfile
3.6%
Shell
2.5%
PowerShell
2.3%