Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TrustPacket

Two exports in, a signed three-way trust reconciliation packet out.

A bookkeeper uploads a trust bank statement and a client ledger. Every line is matched, the three balances are proved against each other, and the output is the four-part packet a bar rule asks for — plus an exceptions page naming the overdrawn client, the stale cheque and the bank charge nobody posted. That exceptions page is the part Clio and MyCase stop short of.

Built for one person running the monthly close for eight to fifteen law firms.

MIT licensed. Runs on one machine with no external services — Node, SQLite, and nothing else.

This is a calculation tool, not legal or accounting advice. It arithmetically proves three balances against each other and prints what it found. It does not know your rules, it cannot certify compliance, and it is not a substitute for the professional who reviews and signs the packet. Verify the retention period and report format your jurisdiction actually requires.

Status, honestly

The reconciliation engine is well covered — 86 tests, including the cases that decide whether a packet can be trusted: uncleared cheques carried across months, unassigned transactions that must break agreement rather than hide, bank charges flagged instead of absorbed, and an empty or partially-read file that refuses to call itself reconciled.

What has not happened: no real firm has run a real export through this. Column mapping is tested against the documented header shapes of Clio, PracticePanther, QuickBooks, Xero, Chase, Bank of America and Wells Fargo, but documented is not the same as observed. No bar reviewer has looked at the packet. Nobody has been billed for it.

If you use it on real books, the first thing worth doing is checking the packet against a reconciliation you already trust.


Running it

npm install
npm start                 # http://127.0.0.1:3000

Open it and press Try the sample files — the demo carries three deliberate problems so the output shows what the tool is for. /?demo=1 walks the whole flow automatically.

With a password, on a server

TRUSTPACKET_PASSWORD='a long passphrase' \
TRUSTPACKET_SECRET="$(openssl rand -base64 32)" \
HOST=0.0.0.0 npm start

Without TRUSTPACKET_PASSWORD the server refuses to bind anything but loopback and exits 1. Client trust records would otherwise be one unauthenticated request away.

Variable Default Meaning
PORT 3000 Listen port
HOST 127.0.0.1 Interface. Anything else demands a password
TRUSTPACKET_PASSWORD unset Operator password. Unset means open, loopback only
TRUSTPACKET_SECRET derived Session signing key. Set it explicitly so sessions survive a restart
TRUSTPACKET_DB data/trustpacket.db Database file, resolved against the install directory

What is public and what is not

The demo is deliberately open so a prospect can put their own file through it without an account. Anything that reads or writes stored records needs a session:

Open Requires sign-in
The sample files Firms, the portfolio, history
Reconciling an uploaded pair Saving a reconciliation
Downloading that packet and CSVs Backup and restore

Naming a firmId on any request makes it a stored-data request.


Records and retention

Trust records are a multi-year obligation, so nothing here is a one-way door.

npm run backup                        # dated snapshot into data/backups, keeps the last 30
node scripts/backup.mjs --keep 90     # or wherever, for however long

Restore is a merge, not a replace: a firm already present by name keeps its identity and only gains the periods it is missing, so running the same restore twice changes nothing the second time. GET /api/backup.json and POST /api/restore do the same over HTTP.

A cron line, if you want it unattended:

17 3 * * *  cd /opt/trustpacket && /usr/bin/node scripts/backup.mjs --keep 60 >> /var/log/trustpacket-backup.log 2>&1

Jurisdiction

Retention wording on the packet follows the firm's configured jurisdiction. California, New York and Florida were checked against their own rules; Texas is from secondary summaries and says so on the document. Anything else asks you to set a period rather than inventing one.

The jurisdiction is frozen into each saved reconciliation, so a firm that later moves does not retroactively change a packet somebody already signed.

None of this is legal advice. The packet states what the firm configured, and a human reviews and signs it.


Deploying

deploy/trustpacket.service is a systemd unit. On the server:

sudo cp deploy/trustpacket.service /etc/systemd/system/
sudo systemctl edit trustpacket          # set the password and secret here, not in the unit
sudo systemctl enable --now trustpacket
curl -s localhost:3000/health            # {"ok":true,"auth":true}

Put it behind a TLS terminator. The session cookie only sets Secure when the request arrives over HTTPS, so terminate properly and forward X-Forwarded-Proto.


Development

npm test              # 86 unit and integration tests
npm run test:ui       # browser pass against a running server on :3000
npm run test:ui:locked# browser pass with a password set, on its own instance
npm run test:a11y     # contrast, names, keyboard, live regions — measured, not assumed
npm run check         # all four
npm run sample        # regenerate the demo files and packets

scripts/ui-check.mjs drives real Chrome and fails on any console error. It has caught bugs the unit tests could not — a timezone fault that filed records under the wrong day, and a dropped click handler — so run it before believing a green suite.

Layout

Path What lives there
src/reconcile.js The three-way reconciliation and the matcher
src/money.js Money and dates. Everything is integer cents
src/parse.js · src/xlsx.js CSV and XLSX ingestion, column guessing
src/store.js SQLite: firms, periods, carry-forward, overrides, backup
src/packet.js · src/detail.js The PDF packet and the working CSVs
src/jurisdictions.js Retention rules, with sources
public/ The browser app

Two things to know before changing anything

Money is integer cents everywhere. The product's promise is that the packet foots to the cent; floats cannot make that promise.

Never new Date(someDateString). The constructor reads "06/30/2026" as local midnight, which east of UTC serialises back as the 29th. That silently filed records under the wrong day once already. Use parseDate from src/money.js.


Contributing

The parts most worth improving, roughly in order of how much they would help someone:

Export formats. tests/realformats.test.js pins the header shapes of the systems this was built against. If your practice management software or bank exports something it cannot read, a fixture in that file plus an alias in src/parse.js fixes it for everyone. A failing fixture is a perfectly good contribution on its own.

Jurisdictions. src/jurisdictions.js carries retention periods with a source and a checked date. California, New York and Florida were read from their own rules; Texas is from secondary summaries and says so on the printed packet. Adding a state means citing where the number came from — an unsourced entry is worse than an absent one, because it prints as though it were authoritative.

The packet itself. It is built to a general reading of a monthly three-way reconciliation. If your bar expects a specific arrangement or an additional schedule, that is worth knowing.

Run npm run check before opening anything: unit tests, two browser passes, and an accessibility audit that has to come back with zero findings.

Why this exists

The monthly three-way trust reconciliation is required by rule in every US state and, for a bookkeeper carrying eight to fifteen firms across four different accounting systems, is still largely done by hand. The tools that do it well assume the firm's books live inside them and charge per firm or per seat, which does not fit somebody serving ten firms they do not own the subscriptions for.

Whether that gap is worth paying to close was never established — nobody was ever asked. The code is here because the problem is real even if the business was not proven.

License

MIT. See LICENSE.

About

Three-way trust reconciliation for law firm client accounts. Two exports in, a signed packet out. Node + SQLite, no external services.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages