EPNExess
Track subscriptions, bills and income across personal and company accounts — offline, in any currency, with tax taken off what comes in.
Install: Windows · macOS · Linux | Download the app | No Python, no database, no account needed
The dashboard: headline rates, this period's actuals, the payment runway with
income below the line, and per-account cover — each card on its own time window.
Why
Subscription trackers assume one person, one currency, and money only going out. That falls apart the moment you run a company alongside your own finances: two sets of accounts, invoices that arrive gross and are worth much less after tax, and one question that actually matters — will the company account have enough in it on the 14th, and if not, how much do I move across?
EPNExess answers that. It runs entirely on your machine: no account, no server, no network calls, no telemetry. Your data is one SQLite file you can copy, back up or delete. It needs nothing outside the Python standard library.
Get it
Just want to use it? Download it from the Releases page and double-click — nothing else to install. Step-by-step instructions with pictures of what to click, for each system, are in INSTALL.md: Windows · macOS · Linux.
With Python installed (3.10+ with Tk — the python.org installers include it):
pip install epnexess # or: pipx install epnexess
epnexess
On recent Linux distributions and Homebrew Python, pip refuses to install
outside a virtual environment; use pipx there. INSTALL.md has
the details per system.
Without Python — download the Windows or macOS build from Releases and run it. Both are unsigned, so the first launch shows a warning: on Windows click More info → Run anyway; on macOS right-click the app → Open once.
From a clone:
git clone https://github.com/Antikatoptis-Pareidolia/Epnexess.git
cd Epnexess && python3 epnexess.py
macOS. Yes, it runs. Install Python from python.org — those builds bundle a current Tk. Then:
python3 epnexess.py
Avoid the /usr/bin/python3 that ships with macOS: it is pinned to an old Tk 8.5
that renders the interface poorly. If you use Homebrew, brew install python-tk
gives the same result. Check what you have with:
python3 -c "import tkinter; print(tkinter.TkVersion)"
8.6 or higher is what you want. To launch it from Finder, save a file called
EPNExess.command next to the app containing
cd "$(dirname "$0")" && python3 epnexess.py, then chmod +x EPNExess.command.
Windows — the python.org installer includes Tk.
Debian / Ubuntu — sudo apt install python3-tk if you get ModuleNotFoundError: tkinter.
Fedora — sudo dnf install python3-tkinter.
To see it populated before entering your own data:
python3 epnexess.py --demo
Add --db /tmp/demo.db to keep the sample data out of your real file.
The screens
Dashboard — an account filter across the top, then four headline figures, then this month, then the runway, then the detail.
The account filter is a checkbox per account in the header row, with subaccounts nested under their group, all ticked by default. Untick one and everything belonging to it — its expenses, its income, its place on the runway, its row in account cover — disappears from every figure on the screen, so you can look at just the personal side, or just one company. The subtitle tells you what you are looking at. It resets to everything on each launch, so you can never leave yourself with a misleadingly small total.
The period cards — this month, upcoming payments, account cover and where it goes — each carry their own window selector: this or last month, this or last quarter, this year, the next 30 or 90 days, or a custom range you type. They are independent, so you can read last month's spending beside the next 90 days of cover. Each figure splits into what has already settled and what is still to come, and only entries dated inside the window count.
Payment runway is a timeline you can set to 14, 30, 45, 60 or 90 days, or to the rest of the month or quarter, using the dropdown in the card's top right. Each day is a column: money going out rises above the line, coloured by the account it leaves from, and income drops below the line in green. Hover any bar for the detail. A heavy week is visible as a shape before you read a single number.
Below that: upcoming movements — each row leads with its due date, then the name, the account it leaves from and a Mark paid / Received button — account cover, and where the money goes by category.
Miss a payment and the card grows an Overdue tab, red, with the count and the sum owed — a weekly bill three weeks late shows as three charges, each at the date it was due, and they stay in every total until you settle or pause them. Paying settles the oldest first, and the Needs attention card jumps straight there when clicked.
Marked something paid by mistake? Payment history on the Currencies & data screen has Mark as unpaid: the payment is removed, the balance goes back, and the charge returns to the schedule as due.
Subscriptions — every recurring entry, income included. Sortable and searchable; filter by expenses, income, one-time or paused. Rows turn amber inside seven days, red when overdue, green for income, grey when paused. Double-click to edit.
Every recurring entry in one sortable table — expenses and income, net of tax,
with one-time entries kept out of the monthly rate.
Accounts — personal and company accounts side by side, each showing what leaves, what arrives, and what is left afterwards.
Subaccounts. An account can sit under another: the three banks your personal money actually lives in, or the cards a company spends from. A group's figures roll up everything beneath it, converting currencies as needed, so you can read the company as one number — and tick Break down by subaccount on the cover card to see exactly which card needs how much by when. Upcoming payments name the subaccount too. Nesting stops at one level on purpose; deeper trees stop being readable at a glance.
Accounts and subaccounts: the three banks personal money actually sits in,
rolled up, with what is left after everything clears.
Currencies & data — base currency, exchange rates, payment history, transfers between accounts, CSV export.
Hand-entered exchange rates (nothing is fetched), settled history with undo, and
money moved between accounts.
Payment history takes a period: pick a month, a quarter, a year, or everything. Only periods your data actually spans are offered, and the line beneath totals what went out, what came in, and the net for that period.
How it works
Expenses and income. Every entry is one or the other. Income replenishes an account on the same weekly, monthly, quarterly or yearly rhythm as a bill, and feeds into account cover so you can see whether the salary lands before the rent leaves. Add one with Add income in the header or on the Subscriptions screen.
Repeats, or not. Each entry has a Repeats checkbox, ticked by default: once the date passes, it rolls to the same day in the next period. Untick it and the entry happens exactly once — it still appears on the runway, in this month's figures and in account cover, but it is left out of the monthly and yearly rates, because a one-off laptop purchase is not a monthly cost. Marking a one-off as paid archives it instead of rolling it forward.
Billing cycles. Weekly, monthly, quarterly, yearly. Every recurring amount is also shown as a monthly equivalent, so a €420 yearly insurance appears as €35 a month and can be compared with everything else. Weekly items use 52/12 weeks per month.
Billing dates use an anchor day. Something billed on the 31st passes through February as the 28th and then returns to the 31st, instead of drifting earlier every year.
Mark paid logs the movement and rolls the date forward one period, so the schedule stays accurate without you editing dates. Overdue items stay visible until you deal with them.
Pause instead of delete. A paused entry keeps its history and drops out of every total.
Base currency and rates. Every total is converted into your base currency. Rates are entered by hand and never fetched online, so the app works offline and your figures do not shift between sessions. Changing the base currency recalculates all stored rates around the new base; the amounts on your entries are never touched.
Tax on income. Company invoices rarely arrive whole. Each income entry takes up to two tax rates that apply one after the other, so 16% profit tax followed by 16% dividend tax leaves 70.56% of the invoice, not 68%. Enter any percentage you like; the presets are only shortcuts. Every total — the runway, this month, account cover, the monthly rate — uses what you keep, while the gross figure stays on the entry so you can still see what was invoiced. Picking a company account suggests a rate; you can clear it.
Account cover. Give an account a balance and the dashboard works out
balance + income − payments over the next 30 days, showing what is left
afterwards and flagging any account that would go under.
Topping up an account. Every account in the cover card has a button: Cover it when it is short, Top up otherwise. It opens with the exact shortfall filled in, lets you pick which account to draw from — personal accounts are listed first — and previews both balances before and after. Confirming moves the money, converting between currencies if the two accounts differ, and both accounts' "left after" figures update immediately. Every move is recorded under Currencies & data.
Balances follow what you mark. Marking something paid or received adjusts the account balance as well as rolling the date forward. This keeps "left after" stable: the payment leaves the balance and leaves the upcoming total at the same time, so the figure only moves when something real changes. If you prefer to type balances straight from your bank, just overwrite them on the Accounts screen.
Keyboard
Ctrl N |
Add subscription |
Ctrl I |
Add income |
Ctrl F |
Search |
Ctrl 1–4 |
Jump to a screen |
F5 |
Refresh |
Esc |
Close a dialog |
On macOS use Control, not Command.
Your data
One SQLite file, by default at ~/.epnexess/epnexess.db. The exact path is shown
at the bottom of the Currencies & data screen. If you ran the earlier version, its
file is moved across automatically the first time you launch.
To back up, copy that file. To keep several sets of books, run with --db and a
different path. Export to CSV writes every entry with its type, recurrence and
monthly equivalent.
Contributing
Bug reports and pull requests are welcome — see CONTRIBUTING.md for the ground rules, the main one being that the core stays offline and dependency-free. Planned work lives in the issues; CHANGELOG.md records what has landed.
python3 tests/test_core.py # logic, no display needed
xvfb-run -a python3 tests/test_ui.py # interface; drop xvfb-run off Linux
Files
| File | What it holds |
|---|---|
epnexess/ |
The package: app.py (window, sidebar), core.py, views.py, widgets.py, theme.py, assets/ |
epnexess.py |
Thin launcher so a clone runs without installing |
tests/ |
Two suites: core logic, and the interface |
tools/ |
Maintainer scripts: icon regeneration. Not needed to run the app |
Licence
PolyForm Noncommercial 1.0.0: use it, copy it, change it and share it freely for any noncommercial purpose. Using it to make money — selling it, bundling it into a paid product or service — needs the author's written permission first; open an issue or get in touch. Contributions are welcomed under the same terms.
Changing the look
Colours live at the top of theme.py as plain hex values: a very light blue page,
white cards, soft navy ink, the logo's teal as the accent, and its yellow for
anything due soon. Green means money arriving; red means overdue or short. To go
back to a blue accent, set ACCENT = "#3D8FD6".
Other things worth knowing:
DashboardView.HORIZONinviews.pysets the runway length (45 days).- Account cover uses a 30-day window; search
account_outlook(30inviews.py. - Fonts are resolved from a preference list in
theme.py(_SANS_STACK,_MONO_STACK) and fall back to whatever your system has. Money is always set in a monospaced face so figures line up in a column. - Icons are drawn as vectors on a canvas in
theme.py, so there are no image files to manage.draw_icontakes a name and a box.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file epnexess-1.1.3.tar.gz.
File metadata
- Download URL: epnexess-1.1.3.tar.gz
- Upload date:
- Size: 479.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1c8eef64f8d2ba4ebb2648c0120b7e0950700a924294475c3257f96f3b8a008c
|
|
| MD5 |
222c89d0e0995d635b5c3ba9fc739a5c
|
|
| BLAKE2b-256 |
59de16682bd8c9ee038dd4e0a4b729694ae8ab916d19de81ad73cdda85d504a3
|
Provenance
The following attestation bundles were made for epnexess-1.1.3.tar.gz:
Publisher:
publish.yml on Antikatoptis-Pareidolia/Epnexess
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
epnexess-1.1.3.tar.gz -
Subject digest:
1c8eef64f8d2ba4ebb2648c0120b7e0950700a924294475c3257f96f3b8a008c - Sigstore transparency entry: 2335036509
- Sigstore integration time:
-
Permalink:
Antikatoptis-Pareidolia/Epnexess@c7f81f3766357c21f544227889180793a993485a -
Branch / Tag:
refs/tags/v1.1.3 - Owner: https://github.com/Antikatoptis-Pareidolia
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7f81f3766357c21f544227889180793a993485a -
Trigger Event:
push
-
Statement type:
File details
Details for the file epnexess-1.1.3-py3-none-any.whl.
File metadata
- Download URL: epnexess-1.1.3-py3-none-any.whl
- Upload date:
- Size: 457.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b781907dbf558aad1d1e7aa7ffafef6aa7c22a44065555122d2382e1efb4c99
|
|
| MD5 |
22b2d38da9cfb27ddcb8f8bb0afdef73
|
|
| BLAKE2b-256 |
40d5d1e0adb9ed6b80b8ac00638f81a5114500d43044a93145536cf6ec6e4e7a
|
Provenance
The following attestation bundles were made for epnexess-1.1.3-py3-none-any.whl:
Publisher:
publish.yml on Antikatoptis-Pareidolia/Epnexess
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
epnexess-1.1.3-py3-none-any.whl -
Subject digest:
1b781907dbf558aad1d1e7aa7ffafef6aa7c22a44065555122d2382e1efb4c99 - Sigstore transparency entry: 2335036518
- Sigstore integration time:
-
Permalink:
Antikatoptis-Pareidolia/Epnexess@c7f81f3766357c21f544227889180793a993485a -
Branch / Tag:
refs/tags/v1.1.3 - Owner: https://github.com/Antikatoptis-Pareidolia
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c7f81f3766357c21f544227889180793a993485a -
Trigger Event:
push
-
Statement type: