Releases: DigiMonk73/BTCTX-MCP
Release list
v1.2.4 · StartOS package 1.2.4:0
Small fixes found by the 1.2.3 test walk on StartOS, with no change to any
tax figure: checked on every test ledger and, before release, on the owner's
own ledger upgraded from 1.2.3 (every row, form, gain and CSV row the same).
The database moves to schema 0005 once (a copy is kept in backups/ first).
Removed
- The unused transaction lock. Nothing in the app could lock a row (no
button, API field or import set it), yet a locked row would have refused
edits and deletes with "not found" while recalculation still changed its
figures. It is gone rather than finished: database schema 0005 drops the
column (the usual copy of the database goes tobackups/first), API
answers no longer carryis_locked, and editing or deleting a missing
transaction says "Transaction not found." No figure changes (checked on
every test ledger). As with any schema change, an older BitcoinTX then
refuses the database; the backup copy is the way back.
Fixed
- Escape closes the transaction panel, like a click outside it: at
once when nothing was changed, else after "Discard changes?". With that
question showing, Escape answers Go back and keeps your changes. The panel
learns of a change at once, so Escape or a click outside right after one
still asks. Found by the 1.2.3 VM walk. - On a phone, every tab in the header fits on screen. At 375 px the
header needed 407 px, so Logout was cut off at the edge ("Logo") and only
reachable by scrolling the header sideways. Up to 480 px wide the tabs are
now a little smaller and spread across the width; they fit from 340 px
(narrower screens still scroll). Found by the 1.2.3 VM walk. - A CSV export re-imported gives back its zero fees. The import dropped
a fee of 0, so rows saved with "0 USD" or "0 BTC" came back with no fee
and no fee currency. Every figure was already the same (a zero fee and no
fee count alike), but a CSV backup now restores exactly what was saved. A
blank fee in a file still means no fee. Found by the 1.2.3 VM walk.
Development
- CI and the release build pin Node 22.23.2 for the StartOS package.
GitHub's runners moved to Node 22.23.3, whose headers fail to build
diskusage, a native module that Start9'smempool-startosdependency
pulls in; the release's package build would have failed the same way. - The web app's error-message helper moved from
hooks/useApiCall.ts
(it held no hooks) intoutils/apiError.ts, beside the other one; its
tests moved unchanged. - The stress tests fail on a refused save instead of carrying on
without the row, except where a random ledger may ask for more than is on
hand, and their random amounts and dates are the same on every run. The
backdating test now checks that its ten backdates were saved. - The CSV export prints numbers from their exact value, not through a
float. Nothing an export writes changes (checked on every test ledger):
a value is already rounded to its column's decimals when it is read, so
the float could only lose digits past about 16, far beyond real amounts. - A release's title names its StartOS package version, for example
"v1.2.3 · StartOS package 1.2.3:0", and so does its StartOS download line:
the number StartOS and the mirror's releases show (the app version plus
the package's own revision). The tag staysvX.Y.Z, which the Mac app,
the Docker image and the AI connector share. v1.2.3's release was
retitled to match.
Downloads
- StartOS:
btctx.s9pk, package 1.2.4:0. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.4(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.4(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.2.3 · StartOS package 1.2.3:0
A maintenance release with no new features. The code behind the app was
reorganized (long functions divided into named steps, the web app's big
components split, filler comments and unused code removed) with one rule:
nothing it produces may change. Every figure, database row, Form 8949 field,
report PDF (byte for byte), CSV, import preview, API answer, AI tool output
and error message was compared with 1.2.2-1's on the golden ledger, the
seed ledger, 40 random ledgers in three timezones and about 2,200 bad
inputs; every page and form was recorded before and after. The only
differences are the two fixes below. Recalculation is about 7% faster.
Fixed
- A CSV row with an unknown
fee_usd_typedvalue (anything but yes, no
or blank) crashed the import preview with a server error; it is now a row
error, "Must be yes, no or blank." Found by the new equivalence check's
bad inputs. - Ledger Review's fee check no longer hides errors. Looking for
transfers whose fee value is off the day's price, it passed over a
transfer on any error while getting that day's price, so a broken
database or a bug looked like "nothing to fix". It now passes over only
a day with no price; anything else is reported as an error.
Development
- Code cleanup, no change in behaviour:
the standards are written down indocs/CODE_STYLE.md, andCLAUDE.md
points to them.scripts/equivalence_check.pycompares everything the
app produces (database rows, API answers, Form 8949 field values, report
PDFs byte for byte, CSV files, import previews, MCP tool outputs, the
error message of each of about 2,200 bad inputs) with v1.2.2-1's, and
times recalculation (docs/TESTING.md). - Every Transaction column is in the CSV export or on a list saying why
not (test_csv_export_columns.py), so a new column can't be left out of
the export unnoticed, as three were before 1.2.1. - Comments and docstrings say what each module is for and why the code
does what it does: no file-path headers, banner dividers, history notes
or narration. The Transaction model's column descriptions no longer call
its main fields "legacy". Two pieces of dead code went:main.py's empty
__main__block and an unusedservices.user.delete_user. - Modern type hints (
list[str],dict,X | None) in the app code,
and ruff'sUPrules keep it that way (tests and migrations excepted). - The complete tax report is built one section at a time
(complete_tax_report.py, from one 510-line function), with the paragraph
styles and grid tables it shares with the transaction history in
reports/pdf_layout.py. Its PDFs are byte-identical. - The transaction history and the Form 8949 field mapping are divided
the same way: the history's columns and widths are one table, its PDF
uses the shared layout; a Form 8949 page's checkbox, row fields and line 2
totals are named steps. Same PDFs, CSVs and field values. - The CSV import guide's script keeps its tables as data and builds
one section per method (backend/scripts/generate_csv_instructions_pdf.py,
from one 395-line function); the PDF it writes is byte-identical, and the
committed one is unchanged. - The CSV import checks a row one field at a time (
csv_import.py:
_validate_rowwas 276 lines), with the account rules per type as a
table and each type's own rules in their own function; the template's
sample rows are data. Two branches that could never run are gone.
Checked on 40,728 generated rows: the same errors, warnings, previews and
transactions. - The River import reads each kind of River row in its own step, finds
duplicates in an exact and a rough pass, and the import endpoint checks,
stubs and saves rows in named steps. Same proposals, warnings, matches and
imports (3,000 random files and 3,000 random ledgers compared). - The dashboard's gains (
get_gains_and_losses, 179 lines) add up
realized gains, proceeds, income by source, fees and this year's gain in
one step each. Same figures on every ledger compared, including ones with
sales this year. - Ledger Review, the CSV export, the AI entry dry run, the public price
download, the IRS form filler, the adoption of a pre-migration database
and the AI connector's request andupdate_transactionare divided
into named steps too; the connector's tools, their schemas and outputs
are unchanged. - The ledger engine (
transaction.py) reads as its steps: create and
edit (check, complete, insert or apply, recalculate), the ledger lines per
kind of transaction, FIFO disposal and transfer lot moves, the input checks
in their fixed order and each type's account rules as a table. Two unused
functions are gone. Same lines, lots, disposals, figures and messages on
every ledger and bad input compared; recalculation no slower. - The transaction form (
TransactionForm.tsx, 1,049 lines) keeps the
form's state, saving and deleting; each transaction type's fields, the
shared account and fee fields, the autofill and the save request are their
own files incomponents/transactionForm/. Every form variant renders the
same labels, roles and HTML (e2e/ui-snapshot.e2e.ts). - The Settings page (799 lines) is its sections: Account, Data
Management (with the CSV import's preview) and Backup & Restore are their
own components, next to the ones it already had; the page keeps the one
action at a time and the message line they share. Same page, labels and
messages. - The River import panel (486 lines) keeps the upload and the import;
the preview, one row and its editable cells are their own components, and
the row logic (the accounts a new type implies, the import request) is in
utils/riverImport.tswith a unit test. The two import previews share
one date format and one "server's reason, else…" message helper. Same
preview after every edit a row allows (recorded before and after). - The Dashboard (470 lines) fetches its figures and lays out four
cards, each its own component; the account totals and the gain/loss
colour are inutils/dashboard.tswith a unit test. Same cards, figures
and HTML. - The rest of the frontend over the limit: the transaction form's
mapping to and from the API is one small step per transaction type (the
same results on 40,000 random forms and transactions, compared), the
Transactions page's sorting, paging and day groups are in
utils/transactionList.ts, Privacy & network's "anything changed?" is
one tested function, and the error message for a failed request is read
in two steps (the same message for 20,000 random failures). - Frontend comments and unused code, as in the backend: no file-path
headers, banner dividers, history notes ("NEW", "ADDED", "legacy") or
comments that repeat the code. Gone because nothing used them: three hook
files and four API hooks (src/hooks/keeps the error message),
formatTimestamp, two types, and five dashboard fields the server has
never sent. The built app is byte-identical but for those five fields. - Lint limits each function's size, so the long functions can't grow
back: Python (ruff: complexity 10, 12 branches, 50 statements, 6 returns;
tests and migrations exempt) and the frontend (ESLint: complexity 15 per
function, 400 lines per file; tests exempt). - The StartOS mirror stays current between releases: a docs-only change
tostartos/goes to DigiMonk73/BTCTX-StartOS right away (CLAUDE.md),
since Start9's build there ignores those files. Anything else waits for a
release. - Roadmap: the AI connector will follow the app's version, chosen over
uvx btctx-mcp@latest.
Downloads
- StartOS:
btctx.s9pk, package 1.2.3:0. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.3(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.3(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.2.2-1
Package-only update; BitcoinTX itself is unchanged.
StartOS package
- The icon is BitcoinTX's own logo, the gold coin the app, the landing
page and the screenshots show, as a 39 KB WebP (startos/icon.webp, under
Start9's 40 KiB limit). It replaces the simplified vector copy drawn for
the size limit (decision of 2026-09-23); Start9's guide asks for the real
asset rather than a redrawn one. The release notes still carry 1.2.2's
for anyone updating from 1.2.1 or earlier.
Documentation
- Screenshots of 1.2.2 in the README and on the landing page (same demo
ledger): the whole taller calculator, 2025 picked in Reports, and the
AI setup prompt with today'suvx btctx-mcp==1.2.2instead of 1.1.0's.
Development
- To-dos are checkboxes. Specific to-dos live in
docs/temp/TODO.md,
the StartOS package's instartos/TODO.md(Start9's standard file, so it
reaches the repository Start9 forks), and what's next indocs/ROADMAP.md.
Every item is- [ ], ticked when done and cleared at the release that
ships it; a finished plan is deleted and its roadmap box ticked.
test_todo_lists.pychecks the format. The 1.2.2 privacy audit is now
docs/PRIVACY_AUDIT.md; the finished stabilization plan is gone. - The StartOS package always keeps Start9's packaging rules, checked by
test_startos_conformance.py: the standard layout, the README's fixed
headings (each opening with a sentence) and no version numbers,
documentation links Start9's indexer can parse, andTODO.mdas Start9's
worklist (# TODO, an item removed when done rather than ticked). Fixed
on the way: the Quick Reference section now opens with a sentence, and
instructions.mdlinks whole files without#anchorsand drops the
package's own README, which isn't upstream documentation. - After Start9 forks the package, releases reach them only by pull
request, and the release makes sure it isn't forgotten. The release
workflow's first job runsscripts/start9-pull.sh --checkand stops before
publishing anything if Start9 changed their fork andstartos/doesn't
have it yet (the mirror sync would undo it). After the mirror push it
opens an issue, "Send vX.Y.Z to Start9", with a link that opens the pull
request ready to create, unless one of ours is still open there (it then
carries the new release). The script finds the fork itself (--fork),
since Start9 renames forks and may use another branch; tests cover it
with local repositories.
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.2(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.2(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.2.2
Stabilization after 1.2.1: no new features. Found by testing the published
1.2.1 on a StartOS VM, a bug hunt of the tax engine, the imports and the AI
connector, and a privacy audit (2026-09-29). Each fix has a test that
failed before it.
Changed
- The sidebar calculator is a little taller (owner's request): its keys
are slightly longer, so it ends level with the Dashboard's Realized
Gains/Losses card. The Sats Converter's price area is now the same height
in Manual, Auto and Date, so the calculator no longer moves when you
switch; in Date mode the day's price sits beside "Select date".
Fixes to tax figures
- Changing a transaction's type no longer keeps the old type's fields.
A Gift changed into a Sell stayed a "gift" and was missing from Form 8949
and Schedule D; a withdrawal changed into an Income deposit reported the
old lots' cost basis as the income instead of that day's value. (Only the
AI connector or the API can change a type; the form locks it.) - A fee entered without its currency now counts. Adding a fee on its
own (e.g. by the AI connector) left a Sell's proceeds and a Buy's cost
basis without it, and took a withdrawal's BTC fee from the balance with
no disposal or value. The fee now gets its type's currency (USD for Buy
and Sell, else the account's) and is saved with it; entries already saved
that way are fixed by the next recalculation (which needs that day's
price, or the fee's value typed in, for a BTC fee). - Pay the AI adds as an Income deposit into Bank or Exchange USD is no
longer valued at the BTC price ($5,000 became $250 million of income);
likewise a Spent or Gift withdrawal from a USD account. Only BTC accounts
are valued at the day's BTC price. - Dates given to the AI connector are read as when adding. Changing a
transaction's date read it as UTC, so in the US a sale moved to Jan 1
landed on Dec 31, in the previous tax year; a date alone is now midday in
your tax timezone. A time ending in " UTC" is read as UTC (it was read in
the tax timezone), and finding transactions by date uses days in your tax
timezone. - A second Buy of the same amount at another price can be saved through
the AI. A Buy of the same BTC amount within 48 hours was taken for a
duplicate even at a different price, skipped, and could never be saved.
It's now a possible duplicate the AI asks you about; only the very same
buy (same amount and price) is skipped. - Export CSV then Import CSV keeps the order of same-time entries. The
import put them in a fixed type order (moves before sales), which could
change which coins a sale used and its gain; rows at the same time now go
in the file's order (the export's is the ledger's). A hand-made file that
lists a sale before the same-time buy paying for it is refused, and the
message says to list the buy first. - Form 8949 line 2 "Totals" is filled. Each page now shows the totals of
its own rows in (d), (e) and (h), which add up to the Schedule D line; it
was left blank on the locked PDF, so it couldn't be typed in either. - A sale drawn from several lots no longer loses cents. Each lot's share
of the proceeds was rounded on its own ($100.00 over three lots showed as
$99.99 on Form 8949); the last share now takes the remainder. - A withdrawal of 1 satoshi more than you hold is refused, like a
transfer or sale. It was accepted and left the balance at -1 sat. - A time with fractions of a second no longer lands in the wrong year.
A sale entered (through the API or the AI connector) half a second after
midnight on Jan 1 counted in the year before; times are now saved in
whole seconds. - A Spent withdrawal changed into a Gift, Donation or Lost no longer keeps
its proceeds, which the complete tax report printed in its Gifts
section. (The network fee is still its own taxable disposal.)
Other fixes
- The Sats Converter no longer shows "BTC Price: $0.00" without a price.
With prices off it says "Prices off", after an error or for a day with no
stored price "No price", and the USD field empties instead of keeping an
old figure (BTC and sats still convert). - My Mempool not answering is an error, not "Prices off". With your
Mempool chosen but missing or not answering (and the fallback off), the
Dashboard said "Prices off"; it now says "Error", and hovering it shows
why (e.g. install and start Mempool). - Clearer messages when no price is stored. A transfer's missing fee
value names the form's Fee value (USD) field (andfee_usdfor
imports), and a new Spent withdrawal without proceeds is no longer told
to "edit that transaction". - The complete tax report says where its prices come from. It claimed
"the average market value at the time of disposal"; it now says values
are the ones entered, a blank one comes from that day's stored daily
price, and dates are in the tax timezone (named). - An import no longer freezes the whole app while it works. A CSV,
River or AI-assistant import (or its preview) held up every other
request, StartOS's health check included, for as long as it ran (10
seconds in one test); that work now runs beside them. - The CSV import reads Excel's "CSV UTF-8" files. Their invisible
byte-order mark made the import say "Missing required columns: date". - A River file row with one comma too many is that row's error. A
trailing comma or an amount written 1,000.00 made the River import fail
with a server error. - A River Sell whose Fee Currency is blank is no longer a fee short. A
blank fee currency on a River Buy or Sell counts as USD, as River charges
it, so the sale lands at River's Received Amount. - When BitcoinTX's address redirects (e.g.
http://tohttps://), the
AI connector says so and which address to set inBTCTX_URL, instead of
an unclear error or a blank price. It still never follows a redirect, so
the AI key only goes to that address. - Keyboard focus shows in the Mac app on older macOS. On Safari before
15.4 (macOS 10.15 to 12.2) no focus ring was drawn at all; the browser's
own ring now stays there, and newer versions keep the gold one.
Privacy (docs/temp/privacy-audit.md)
- The AI connector no longer writes request addresses to the AI app's log
files; past-price lookups named your transaction dates there. - An
.onionmempool address needs the proxy: it's refused without one (it
was looked up through the normal DNS, which then saw the name). - An encrypted backup no longer passes through a plain copy in the system's
temp folder, and a restore's decrypted file is owner-only from the moment
it's written. - Making reports no longer logs how many transactions, disposals and lots
each year has (now only at the DEBUG log level). - The Docker instructions start BitcoinTX reachable from your computer
only (-p 127.0.0.1:8080:80), with a note to put it behind HTTPS for
other devices; the old command sent your password and ledger unencrypted
across your network. The AI connector warns once in its log when it talks
to another machine over plainhttp://(owner decision). - The encrypted backup's password is typed in a hidden field, twice: it
was a plain pop-up that showed it and asked once, so a typo made a backup
no one could open (owner decision). - Deleted transactions are erased from the database file (SQLite's secure
delete), instead of staying readable in it and its copies until it was
compacted. - Settings > Backup & Restore says BitcoinTX keeps a few unencrypted safety
copies of its database in its data folder. - Responses no longer name the web server ("server: uvicorn"), and the
Docker image's data folder is owner-only (it was writable by everyone). - The READMEs say that
uvxdownloads the AI connector from PyPI once,
which sees your IP address and the version. - Settings (and StartOS's Price Source & Privacy action and instructions)
name every public site BitcoinTX may contact (mempool.space and Coinbase
were missing) and say the sites see your IP address and when BitcoinTX is
open, never your transaction dates (owner decision). - Fewer requests to public price sites: the live price asks Kraken first
and CoinGecko only if Kraken fails (CoinGecko refuses VPN and Tor users,
so each refresh used to contact both), and the block height is kept for a
minute like the price instead of being asked on every Dashboard visit
(owner decision). - Restoring a backup in the app keeps the price settings in use, as it
keeps your login and AI key: a backup from before a switch to Tor or Off
brought back direct lookups of the public sites (owner decision).
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.2(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.2(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.2.1
Fixes
-
Adding transactions before choosing a price source no longer turns
public price sites on. Since 1.1.0, an install with transactions but no
price choice yet was taken for one from before 1.1.0 (when public sites
were on by default), so its next restart, e.g. an update, switched it to
Public price sites and asked them for prices, although you never
chose that. Now only an install that stored past prices before 1.1.0
keeps public sites; any other stays unasked until you choose. If you
added entries before answering the price question and BitcoinTX has
restarted since, check Settings → Privacy & Network (on StartOS, the
Price Source & Privacy action decides instead). Found testing the 1.2.0
package on a StartOS VM. -
The complete tax report works with price lookups off. It looked up
the Jan 1 BTC price even when nothing was held then, and a missing price
failed the whole report; a missing Dec 31 price valued the holdings at
$0. Holdings with no price for that day now read "not priced", and the
rest of the report is unchanged. -
The complete tax report's Beginning of Year Holdings were wrong
whenever BTC had moved or been sold in earlier years: lots were counted
twice (in the checklist's ledger, 1.8797 BTC on Jan 1, 2024 instead of
the 0.9898 held on Dec 31, 2023). They are now taken the same way as the
year-end holdings, so Jan 1 always matches the previous Dec 31. Only this
section of that report was affected: IRS forms and gains were right. -
Export CSV now imports back to the same ledger. The export had no
column for a BTC fee's USD value, a gift's fair market value or the
Broker form override (all added after the export was written), so a
re-import priced every BTC fee again at that day's price, changing gains,
lost Broker form choices and failed outright with price lookups off. The
export, the import and the template now share one list of columns, with
three new optional ones at the end:fee_usd,fmv_usdand
broker_reporting. Older CSV files import as before. A CSV import a row
can't be saved from (e.g. a BTC fee with no price to value it) now says
why instead of answering with a server error. The CSV import guide
describes the new columns and no longer calls a BTC deposit's basis
optional. -
Deleting a transaction that later ones depend on (a buy whose BTC a later
sell or transfer spends) is refused and changes nothing. Before, it showed
an error but deleted the row anyway, and every later save then failed
until the row was entered again. The message now says why: "Not deleted:
later transactions depend on this one. …". -
Restoring a backup made before 0.9.2 with the wrong password now always
says "Wrong password?". About 1 time in 256 it said the file wasn't a
BitcoinTX backup instead (the live database was never touched either way). -
The log is readable again. Every health check (StartOS asks every 30
seconds) logged two database lines, half of a running install's log. It
now logs nothing. -
Loading the dashboard asked for the live price twice (the dashboard and
the sidebar converter at the same moment). Requests at the same moment
now share one. -
The dashboard's BTC Cost Basis is now labeled Avg. Cost per BTC:
it always was the average cost of one bitcoin you hold, not your total
cost. -
The login page shows Create account only on a fresh install that
still has the default login. Once the account is set up, that page
resets it (deleting every transaction, after asking for the current
password), which the link didn't say; Settings still has Reset
Username & Password. -
A report that can't be made now says why. Reports showed only "Failed
to generate the report" and dropped the reason. They now show it, and
when an old withdrawal's network fee (or a Spent withdrawal's proceeds)
has no USD value and no price is available, the message names that
transaction, e.g. "Withdrawal of 0.01 BTC on 2024-06-01: its network fee
has no USD value (edit that transaction to enter it)". -
Restoring a backup file in the app keeps your current login. It
brought back the backup's username and password, so a password changed
since stopped working, and on StartOS Show Credentials showed one that
no longer logged in. Like the AI key, the login in use now stays; the
ledger and settings come from the backup. StartOS's own backups were
never affected (they restore the login and the ledger together). -
With prices off, the dashboard's Unrealized Gains/Losses said
"Loading..." forever; it now says "Prices off" (or "No price" when a
lookup failed). -
When public price sites go through a proxy that's down (e.g. Tor
stopped), the error says so and asks whether Tor is running, instead of
only "No public site answered". -
Deleting a transaction asks "Are you sure?" once, not twice.
-
Export CSV has a fourth new optional column,
fee_usd_typed: whether
a BTC fee's USD value was typed (yes) or that day's price (no). A
re-import marked every fee value as typed, so editing such a
transaction's date later kept the old value instead of pricing it
again. Files without the column import as before. -
A missing file (the browser's
/favicon.ico, an outdated script after an
update) is a 404 instead of the app's page. -
Sats Converter: a price you type in Manual mode is no longer replaced
by the starting price arriving late (on a slow server it could land after
you had typed yours).
Development
docs/AGENT-TESTS.md: the release tests an AI agent runs on a StartOS VM
before each release (install, actions, the update from the last release,
backups, price sources, TLS, the MCP connector, the Mac app), against a
14-transaction ledger whose every figure is known, with its known issues.- Dependabot opens weekly pull requests against
develop, grouped per
directory (Python inbackend/,mcp_server/and the dev tools, npm in
frontend/andstartos/, GitHub Actions). It leaves the StartOS SDK and its service
packages,@playwright/testand the deferred upgrades alone
(docs/MAINTENANCE.md, "Dependabot"). - First updates taken: uvicorn 0.54.0; vitest 5.0.2,
eslint-plugin-react-refresh 0.5.7, globals 17 (frontend); prettier, ncc and
the Node types (StartOS package build); GitHub Actions on their current
majors (Node 24). - The release job downloads its two artifacts by name, so a Docker build
record can't get mixed in. make previewruns the checked-out code in a browser on a throwaway
database with offline test prices;make docker-smokebuilds the Docker
image locally and runs CI's container checks on it (docs/TESTING.md).
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.1(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.1(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.2.0
StartOS: your own Mempool and Tor, chosen in one action
- New action: Price Source & Privacy. Choose My Mempool on this
server, Public price sites (optionally over Tor), Off, or
Choose in BitcoinTX. A fresh install asks right after Show
Credentials; updating installs get an optional reminder. What you choose
there shows read-only in the app (Settings → Privacy & Network). - Your Mempool on the same server now just works. BitcoinTX reaches it
inside StartOS, so there's no address to copy, no https certificate that
fails, and no LAN address that can change. This replaces the 1.1.0 advice
to turn on the fallback for.local/https addresses (which, with the
fallback off, meant no prices at all). Mempool and Tor are optional
dependencies, only while you use them. - Tor for public sites: with the Tor service installed, requests to
public price sites go through it, so they never see your IP address. If
Tor stops, those requests fail rather than go out directly. - Old default logins retired. An install from before generated passwords
that still had admin/password gets a generated password at this update,
and StartOS asks you to copy it (Show Credentials) before starting. - Translated: every action, task and message of the package in Spanish,
German, Polish and French (the app itself stays English, US tax forms). - The package follows Start9's conventions for the community registry: its
mirror (DigiMonk73/BTCTX-StartOS) runs Start9's standard build and release
workflows, and changes Start9 makes to their fork come back with
scripts/start9-pull.sh.
Price settings the server can set
BTCTX_PRICE_SOURCE,BTCTX_MEMPOOL_URL,BTCTX_MEMPOOL_FALLBACKand
BTCTX_PROXY_URL(Docker too): when set, they replace Settings →
Privacy & Network, which shows them read-only; your stored settings come
back once they're removed. An invalid value turns lookups off. See
docs/STARTOS_COMPATIBILITY.md.- The log now says where each download of past prices came from ("Price
history from your mempool server: N days", "… from public site bitstamp"),
so you can check no public site was asked.
Fixes
- Recalculate Ledger (StartOS action,
python -m backend.cli) can look up
a missing day's price. The maintenance commands never read the price
settings, so they behaved as if no source was chosen.
AI connector on PyPI
- The connector is published to PyPI as
btctx-mcpwith every release, by
trusted publishing (no token is stored anywhere). The setup prompt,
configurations, StartOS action and docs now install it with
uvx btctx-mcp==X.Y.Z, still pinned to your BitcoinTX version; nothing
needsgiton your computer any more. Versions before 1.2.0 stay
installable from GitHub (uvx --from "git+…@vX.Y.Z#subdirectory=mcp_server" btctx-mcp).
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.2.0(amd64 and arm64). - AI connector:
uvx btctx-mcp==1.2.0(PyPI); Settings → Connect an AI Assistant has it ready to paste.
v1.1.0
Privacy: you choose where prices come from, and no lookup reveals a date
- Nothing is contacted until you choose. A fresh install asks, right
after the first login, where Bitcoin prices come from: My mempool
server, Public price sites, or Off (Settings → Privacy &
Network). Before that the dashboard says "Prices off". Installs from
before 1.1.0 keep what they did. - Your own mempool server now also gives past prices (its hourly record
at 00:00 UTC), and it's used only: the public sites are asked when it
can't answer only if you turn on Fall back to public price sites. A
mempool server on your network is reached directly, not through the proxy
(an .onion still is). - Past prices never reveal your dates. Before, a missing day started a
~1,000-day download that began exactly 500 days before it, so the site
could work out the date, and the Coinbase backup never covered the day,
so BitcoinTX often asked for the day by name. Now the public sites send
the whole daily history once, in the same requests for every install, and
after that only "the latest days". The single-day lookups (CoinGecko,
Kraken, CoinDesk) are gone. Days before 2011-08-18 (Bitstamp's first) have
no public price: type the value in. - The live price is asked at most once a minute however many tabs are open,
and the sidebar converter doesn't ask while its tab is hidden. - Correction: the 1.0.3 README, StartOS docs and website said that
download kept lookups from pointing at your dates; it didn't, as above. - StartOS: a mempool address starting with
https(the.localones)
doesn't answer BitcoinTX yet (it can't check StartOS's own certificate);
1.0.3 then quietly used the public sites. Turn on Fall back to public
price sites with such an address for now; a direct connection inside
StartOS comes next.
Security
- PDF reports can't be made to fetch anything. Text stored in a
transaction (for example from an imported file or an AI) could make the
server contact any web address while drawing a report, skipping the
proxy. Report text is now escaped and the PDF library may fetch nothing
remote. A deposit's source must be one of the listed values. The IRS forms
(filled by a different tool) are unchanged. - Docker/source first run: a setup code. Until you set your own login,
logging in with admin/password or claiming the account needs a one-time
code from the log (docker logs) or/data/setup-code.txt, so nobody
else on the network can claim it first. An older install that still uses
admin/password: the login page asks for the code; log in, then change the
password in Settings (your transactions are untouched). - Login protection: repeated wrong passwords make everyone wait longer
(1 s up to 5 min); new passwords need at least 12 characters (existing
ones still work); changing the password needs the current one. - Requests from other sites are refused: a page on another site (or
another app on the same server) can't make your browser restore a backup
or import a file. Cross-origin requests (CORS) are off unless configured. - A restore refuses files over 1 GiB and unreasonable key-strength settings;
two old example session keys from the project's history are refused; an
empty session key file is replaced. - CSV exports can't start a spreadsheet formula; Docker and StartOS keep no
access log (request paths can hold dates), and transaction dates left the
normal log.
AI connector
- The setup texts pin the connector to your BitcoinTX version (
@vX.Y.Z)
instead of followingmain: your AI app runs exactly that code and
fetches nothing new at each start. After an update, every tool reply says
which version to set. (PyPI will come once publishing is set up.)
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.1.0(amd64 and arm64).
v1.0.3
AI keys instead of passwords
- The AI connector no longer uses your password. On Docker and StartOS,
Settings → Connect an AI Assistant now has the Let AI assistants use
BitcoinTX switch (off by default) and Create AI key: a key shown
once, which you paste into your AI app's settings. New key replaces
it, Revoke deletes it. BitcoinTX stores only a hash of it. The Mac app
keeps its automatic key file. - The key can do only what the AI tools need: read the ledger, add,
change or delete single entries, recalculate, and make a backup copy. It
can't log in, change the username or password, restore or download a
backup, export or import files, delete everything, apply Ledger Review
fixes, open reports or change settings: those answer 403 (an allow-list,
so anything added later is closed to the key too). - New AI tool
backup_ledger: a copy of the database in BitcoinTX's
backupsfolder on your server before a big change (the newest 3 are
kept, apart from the pre-upgrade copies; one a minute). - A restore keeps the current AI key and switch, so an old backup can't
bring back a key you revoked. - The connector refuses a password. While
BTCTX_PASSWORDis in its
settings, every tool says how to switch to a key and sends nothing. - Breaking: the undocumented
API_KEYsetting (X-API-Keyheader) is
gone; use an AI key. - The StartOS Connect an AI Assistant action no longer shows your login;
its configuration has aYOUR_BITCOINTX_AI_KEYplaceholder.
AI connector updates itself
- The setup prompt, configs and docs install the connector from this repo's
mainbranch, which now holds released code only: uvx checks it each time
the AI app starts, so you never edit a version again. Pinning@vX.Y.Z
still works. - The connector has the app's version number, and when the two differ every
tool reply starts with a line saying so and what to do (restart the AI
app, or update BitcoinTX).
If you use an AI assistant with BitcoinTX on Docker or StartOS: your AI
app's settings file held your BitcoinTX password in plain text. After
upgrading: (1) in BitcoinTX, Settings → Connect an AI Assistant, turn on AI
access and create an AI key; (2) in your AI app's settings, replace
BTCTX_PASSWORD (and BTCTX_USERNAME) with BTCTX_AI_KEY set to that key,
and delete the password; (3) change your BitcoinTX password (Settings →
Reset Username & Password), or on StartOS run Reset Login Credentials,
because the old one sat in that file. Mac app users: nothing to do.
Documentation
- The MCP README no longer says a local model means "nothing leaves your
computer": what the AI reads stays there, but BitcoinTX still looks up
prices unless Live data is off. It also says a preview can look up prices. - Correction: v0.9.2 said past-day price requests "no longer name
individual transaction dates", and the README said lookups "don't reveal
your transaction dates". A missing day is filled by one download of about
1,000 days, but if that download fails BitcoinTX asks CoinGecko, Kraken or
CoinDesk for the single day, which names it. The README now says so; Live
data off sends no request at all. - The StartOS listing says BitcoinTX sends your ledger nowhere (instead of
"your ledger stays on your server", which a cloud AI would contradict), in
all five languages. The README and StartOS docs list every outside service
(single-day fallback and block-height sources included). docs/startos-research/btctx-requirements.md(it described v0.8.0) moved to
docs/archive/.
Development
- Work now happens on
develop;mainholds released code only and moves
by fast-forwarding todevelop(CLAUDE.md, "Branches"). The pre-push
hook refuses a push tomainof anything not already ondevelop, and
any delete, rewind or force-push ofmain; the release workflow refuses a
release commit that isn't onmain.
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.0.3(amd64 and arm64).
v1.0.2-1
Package-only update; BitcoinTX itself is unchanged.
- The StartOS store listing and release notes are also in Spanish, German,
Polish and French. The app stays in English and produces US (IRS) tax
forms, which the listing says. - The listing's website link points to the BitcoinTX site.
- The package README names no versions (Start9's rule): old upgrade paths
are described by what changed.
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.0.2(amd64 and arm64).
v1.0.2
AI assistant privacy
- Settings → Connect an AI Assistant now opens with a warning: the AI's
model reads what BitcoinTX hands it (transactions, balances, gains) and
whatever you paste, and with a cloud AI (Claude, Grok…) that goes to the
provider. It points to local-model apps (LM Studio, Goose with Ollama). - The AI setup guide tells the AI to say this before it installs anything;
the MCP README has a "Privacy: cloud or local model" section with LM Studio
and Goose setup; the README and the StartOS instructions carry the warning. - Mac app: AI assistant access is now off by default, including on
installs upgraded from 0.9.2–1.0.1 that never touched the switch. If you
use an AI with the Mac app, turn on Settings → Connect an AI Assistant →
Let AI assistants use BitcoinTX once; until then the AI gets "AI
assistant access is turned off". A line under the switch says what it
allows. The docs say AI entry is optional, what the switch
does, and that Docker/StartOS have no switch (the server logs in with the
password). - StartOS: the MCP API address, the Connect an AI Assistant action and the
package description say the same, and name LM Studio. Text only; AI access
on StartOS still works by username and password, as before.
Documentation
- Install from the release downloads (macOS
.dmg,btctx.s9pk, the
ghcr.io/digimonk73/btctx-mcpimage) instead of building; README features
now include Ledger Review, stored price history and Privacy & Network; the
upgrade steps use Ledger Review; price and outbound-request sources updated. - The AI's ledger guide: income with no price available is refused (ask the
user), a withdrawal's BTC fee is taxable even for Gift, Donation and Lost,
River's Received is net of its fee, and when to usereview_ledger. - Mac app docs: the port-busy dialog replaces the old random-port fallback,
the MCP server needs no settings, Gatekeeper steps for macOS 15. - Developer docs: test counts and what
make checkcovers,make e2e, the
weekly workflows, new modules inCLAUDE.md, missing pinned packages,
network-fee disposals on withdrawals in the IRS docs, frozen StartOS ids.
The finished hardening/redesign and StartOS package plans moved to
docs/archive/; the roadmap lists only open work.
Releases
- The release workflow now also publishes each version's
btctx.s9pkas a
release on the StartOS mirror (DigiMonk73/BTCTX-StartOS), marked Latest,
so the mirror's releases page stays current with no work in that repo
(scripts/mirror-startos-release.sh). - The mirror's only branch is now
main(wasmaster, plus four stale
branches); the sync script and the mirror's CI follow it.
Downloads
- StartOS:
btctx.s9pk. In StartOS, open Sideload and upload it; installs of earlier versions update in place. - macOS:
BitcoinTX-macOS.dmg(or the.zip). The app is not signed by Apple: the first time, right-click (Control-click) BitcoinTX in Applications and choose Open; on macOS 15 or later, open it once, then go to System Settings > Privacy & Security and click Open Anyway. - Docker:
ghcr.io/digimonk73/btctx-mcp:v1.0.2(amd64 and arm64).