pledge is a double-entry ledger that runs inside PostgreSQL, with a Django package on top. If your app moves money, credits or points, it keeps the books for you:
- Balanced by design. Every transfer writes two entries that sum to zero.
- History built in. Each entry stores the running balance, so you can ask for any past balance.
- Safe retries. Send your own transfer id; the same transfer twice is booked once.
- Tamper-resistant. Ledger rows can't be deleted or edited; accounts are closed, never removed.
- Fast. Written in Rust, one SQL statement per transfer or batch.
Django first
With django-pledge the ledger looks like ordinary Django models:
pip install django-pledge
INSTALLED_APPS = [..., "django_pledge"]
from decimal import Decimal
from django_pledge.models import Account, Transfer
cash = Account.objects.create(name="cash", currency="EUR")
alice = Account.objects.create(name="alice", currency="EUR", allow_negative_balance=False)
cash.transfer(alice, Decimal("10.00"))
Transfer.objects.bulk_create([
Transfer(from_account=alice, to_account=cash, amount=Decimal("2.50")),
Transfer(from_account=cash, to_account=alice, amount=1),
]) # one atomic batch, one statement
alice.refresh_from_db()
alice.balance # Decimal('8.50')
alice.history() # every entry, with its running balance
Overdraw Alice's account and you get a typed exception, NegativeBalanceNotAllowed, with the account id attached. Store a TransferId with your own record before you call the ledger, and a retry books nothing twice. Link your own models with a normal ForeignKey to pledge.Account.
Or plain SQL
Django is optional. Everything is a function in the pledge schema:
CREATE EXTENSION pledge;
SELECT id FROM pledge.create_account('bank', 'USD');
SELECT id FROM pledge.create_account('alice', 'USD', allow_negative_balance => false);
SELECT * FROM pledge.create_transfer('pla_…bank', 'pla_…alice', 100.00);
SELECT name, balance FROM pledge.accounts_view;
SELECT * FROM pledge.verify(); -- every check should say ok
Fast, and checked
pledge grew out of pgledger by pgr0ss, and we still benchmark against it:
pledge is about 1.5 times faster per transfer and about 2 times faster for batches. Speed only counts if the books stay straight, so pledge also passes a bank test where connections are killed mid-transfer, a model-based test, and a replay of a real bank's million transactions.
It's a young project, so the API may still change before 1.0. It runs on PostgreSQL 17 and 18.
Try it
- Docs: https://pgpledge.github.io/pledge/
- Tutorial, build a small currency exchange in Django: https://pgpledge.github.io/pledge/django/exchange/
- Repository: https://github.com/pgpledge/pledge
- PyPI: https://pypi.org/project/django-pledge/
Clone the repo and run just example to click through the exchange in your browser. Feedback and issues are very welcome.
Top comments (0)