1
0
Fork 0
forked from aiolabs/libra

Rename Castle Accounting extension to Libra

Full identifier rename: module path lnbits.extensions.castle →
lnbits.extensions.libra, DB ext_castle → ext_libra, URL prefix
/castle/ → /libra/, manifest id castle → libra, fava ledger slug
default castle-ledger → libra-ledger, Beancount source metadata
castle-api → libra-api and link prefixes castle-{entry,tx}- →
libra-{entry,tx}-, column castle_wallet_id → libra_wallet_id, all
Python/JS/HTML identifiers (castle_ext, CastleSettings,
castle_reference, castleWalletConfigured, etc.).

Display name "Castle Accounting" → "Libra" (the scales/balance
metaphor — fits double-entry bookkeeping).

No backward compat: production hosts will be force-updated. Old
castle-prefixed Beancount metadata in existing Fava ledgers is
historical; new entries use libra-* prefixes going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Padreug 2026-05-05 10:24:46 +02:00
commit c174cda48d
44 changed files with 953 additions and 953 deletions

View file

@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview
Castle Accounting is a double-entry bookkeeping extension for LNbits that enables collectives (co-living spaces, makerspaces, community projects) to track finances with proper accounting principles. It integrates Lightning Network payments with traditional accounting, supporting both cryptocurrency and fiat currency tracking.
Libra is a double-entry bookkeeping extension for LNbits that enables collectives (co-living spaces, makerspaces, community projects) to track finances with proper accounting principles. It integrates Lightning Network payments with traditional accounting, supporting both cryptocurrency and fiat currency tracking.
## Architecture
@ -12,9 +12,9 @@ Castle Accounting is a double-entry bookkeeping extension for LNbits that enable
**Double-Entry Accounting**: Every transaction affects at least two accounts. Debits must equal credits. Five account types: Assets, Liabilities, Equity, Revenue (Income), Expenses.
**Fava/Beancount Backend**: Castle now uses [Fava](https://github.com/beancount/fava) as the primary accounting engine. Fava is a web interface for Beancount that provides a REST API for ledger operations. All accounting calculations (balance sheets, trial balances, account reports) are delegated to Fava/Beancount. Castle formats transactions as Beancount entries and submits them via Fava's API.
**Fava/Beancount Backend**: Libra now uses [Fava](https://github.com/beancount/fava) as the primary accounting engine. Fava is a web interface for Beancount that provides a REST API for ledger operations. All accounting calculations (balance sheets, trial balances, account reports) are delegated to Fava/Beancount. Libra formats transactions as Beancount entries and submits them via Fava's API.
**Required External Dependency**: Fava must be running as a separate service. Configure `fava_url` and `fava_ledger_slug` in Castle settings (default: `http://localhost:3333` with slug `castle-accounting`). Castle will not function without Fava.
**Required External Dependency**: Fava must be running as a separate service. Configure `fava_url` and `fava_ledger_slug` in Libra settings (default: `http://localhost:3333` with slug `libra-accounting`). Libra will not function without Fava.
**Pure Functional Core**: The `core/` directory contains pure accounting logic independent of the database layer:
- `core/validation.py` - Entry validation rules
@ -44,12 +44,12 @@ Castle Accounting is a double-entry bookkeeping extension for LNbits that enable
- `tasks.py` - Background tasks (invoice payment monitoring)
- `account_utils.py` - Hierarchical account naming utilities
- `fava_client.py` - HTTP client for Fava REST API (add_entry, query, balance_sheet)
- `beancount_format.py` - Converts Castle entries to Beancount transaction format
- `beancount_format.py` - Converts Libra entries to Beancount transaction format
- `core/validation.py` - Pure validation functions for accounting rules
### Database Schema
**Note**: With Fava integration, Castle maintains a local cache of some data but delegates authoritative balance calculations to Beancount/Fava.
**Note**: With Fava integration, Libra maintains a local cache of some data but delegates authoritative balance calculations to Beancount/Fava.
**journal_entries**: Transaction headers stored locally and synced to Fava
- `flag` field: `*` (cleared), `!` (pending), `#` (flagged), `x` (void)
@ -57,10 +57,10 @@ Castle Accounting is a double-entry bookkeeping extension for LNbits that enable
- `reference` field: Links to payment_hash, invoice numbers, etc.
- Enriched with `username` field when retrieved via API (added from LNbits user data)
**extension_settings**: Castle wallet configuration (admin-only)
- `castle_wallet_id` - The LNbits wallet used for Castle operations
**extension_settings**: Libra wallet configuration (admin-only)
- `libra_wallet_id` - The LNbits wallet used for Libra operations
- `fava_url` - Fava service URL (default: http://localhost:3333)
- `fava_ledger_slug` - Ledger identifier in Fava (default: castle-accounting)
- `fava_ledger_slug` - Ledger identifier in Fava (default: libra-accounting)
- `fava_timeout` - API request timeout in seconds
**user_wallet_settings**: Per-user wallet configuration
@ -70,22 +70,22 @@ Castle Accounting is a double-entry bookkeeping extension for LNbits that enable
## Transaction Flows
### User Adds Expense (Liability)
User pays cash for groceries, Castle owes them:
User pays cash for groceries, Libra owes them:
```
DR Expenses:Food 39,669 sats
CR Liabilities:Payable:User-af983632 39,669 sats
```
Metadata preserves: `{"fiat_currency": "EUR", "fiat_amount": "36.93", "fiat_rate": "1074.192"}`
### Castle Adds Receivable
User owes Castle for accommodation:
### Libra Adds Receivable
User owes Libra for accommodation:
```
DR Assets:Receivable:User-af983632 268,548 sats
CR Income:Accommodation 268,548 sats
```
### User Pays with Lightning
Invoice generated on **Castle's wallet** (not user's). After payment:
Invoice generated on **Libra's wallet** (not user's). After payment:
```
DR Assets:Lightning:Balance 268,548 sats
CR Assets:Receivable:User-af983632 268,548 sats
@ -101,14 +101,14 @@ DR Liabilities:Payable:User-af983632 39,669 sats
## Balance Calculation Logic
**User Balance** (calculated by Beancount via Fava):
- Positive = Castle owes user (LIABILITY accounts have credit balance)
- Negative = User owes Castle (ASSET accounts have debit balance)
- Positive = Libra owes user (LIABILITY accounts have credit balance)
- Negative = User owes Libra (ASSET accounts have debit balance)
- Calculated by querying Fava for sum of all postings across user's accounts
- Fiat balances calculated by Beancount from cost basis annotations, NOT converted from current sats
**Perspective-Based UI**:
- **User View**: Green = Castle owes them, Red = They owe Castle
- **Castle Admin View**: Green = User owes Castle, Red = Castle owes user
- **User View**: Green = Libra owes them, Red = They owe Libra
- **Libra Admin View**: Green = User owes Libra, Red = Libra owes user
**Balance Retrieval**: Use `GET /api/v1/balance` which queries Fava's balance sheet or account reports for accurate, Beancount-calculated balances.
@ -127,12 +127,12 @@ DR Liabilities:Payable:User-af983632 39,669 sats
- `POST /api/v1/entries` - Create raw journal entry (admin only)
### Payments & Balances
- `GET /api/v1/balance` - Get user balance (or Castle total if super user)
- `GET /api/v1/balance` - Get user balance (or Libra total if super user)
- `GET /api/v1/balances/all` - Get all user balances (admin, enriched with usernames)
- `POST /api/v1/generate-payment-invoice` - Generate invoice for user to pay Castle
- `POST /api/v1/record-payment` - Record Lightning payment from user to Castle
- `POST /api/v1/generate-payment-invoice` - Generate invoice for user to pay Libra
- `POST /api/v1/record-payment` - Record Lightning payment from user to Libra
- `POST /api/v1/settle-receivable` - Manually settle receivable (cash/bank)
- `POST /api/v1/pay-user` - Castle pays user (cash/bank/lightning)
- `POST /api/v1/pay-user` - Libra pays user (cash/bank/lightning)
### Manual Payment Requests
- `POST /api/v1/manual-payment-requests` - User requests payment
@ -148,8 +148,8 @@ DR Liabilities:Payable:User-af983632 39,669 sats
- `POST /api/v1/tasks/daily-reconciliation` - Run daily reconciliation (admin)
### Settings
- `GET /api/v1/settings` - Get Castle settings (super user)
- `PUT /api/v1/settings` - Update Castle settings (super user)
- `GET /api/v1/settings` - Get Libra settings (super user)
- `PUT /api/v1/settings` - Update Libra settings (super user)
- `GET /api/v1/user/wallet` - Get user wallet settings
- `PUT /api/v1/user/wallet` - Update user wallet settings
@ -213,7 +213,7 @@ entry = format_transaction(
{"account": "Liabilities:Payable:User-abc123", "amount": "-50000 SATS"}
],
tags=["groceries"],
links=["castle-entry-123"]
links=["libra-entry-123"]
)
# Submit to Fava
@ -241,15 +241,15 @@ balance_result = await client.query(
### Extension as LNbits Module
This extension follows LNbits extension structure:
- Registered via `castle_ext` router in `__init__.py`
- Registered via `libra_ext` router in `__init__.py`
- Static files served from `static/` directory
- Templates in `templates/castle/`
- Database accessed via `db = Database("ext_castle")`
- Templates in `templates/libra/`
- Database accessed via `db = Database("ext_libra")`
**Startup Requirements**:
- `castle_start()` initializes Fava client on extension load
- `libra_start()` initializes Fava client on extension load
- Background task `wait_for_paid_invoices()` monitors Lightning invoice payments
- Fava service MUST be running before starting LNbits with Castle extension
- Fava service MUST be running before starting LNbits with Libra extension
## Common Tasks
@ -282,7 +282,7 @@ entry = format_transaction(
{"account": "Assets:Lightning:Balance", "amount": "-50000 SATS"}
],
tags=["utilities"],
links=["castle-tx-123"]
links=["libra-tx-123"]
)
client = get_fava_client()
@ -337,24 +337,24 @@ result = await client.query(query)
### Prerequisites
1. **LNbits**: This extension must be installed in the `lnbits/extensions/` directory
2. **Fava Service**: Must be running before starting LNbits with Castle enabled
2. **Fava Service**: Must be running before starting LNbits with Libra enabled
```bash
# Install Fava
pip install fava
# Create a basic Beancount file
touch castle-ledger.beancount
touch libra-ledger.beancount
# Start Fava (default: http://localhost:3333)
fava castle-ledger.beancount
fava libra-ledger.beancount
```
3. **Configure Castle Settings**: Set `fava_url` and `fava_ledger_slug` via settings API or UI
3. **Configure Libra Settings**: Set `fava_url` and `fava_ledger_slug` via settings API or UI
### Running Castle Extension
### Running Libra Extension
Castle is loaded as part of LNbits. No separate build or test commands are needed for the extension itself. Development workflow:
Libra is loaded as part of LNbits. No separate build or test commands are needed for the extension itself. Development workflow:
1. Modify code in `lnbits/extensions/castle/`
1. Modify code in `lnbits/extensions/libra/`
2. Restart LNbits
3. Extension hot-reloads are supported by LNbits in development mode
@ -363,13 +363,13 @@ Castle is loaded as part of LNbits. No separate build or test commands are neede
Use the web UI or API endpoints to create test transactions. For API testing:
```bash
# Create expense (user owes Castle)
curl -X POST http://localhost:5000/castle/api/v1/entries/expense \
# Create expense (user owes Libra)
curl -X POST http://localhost:5000/libra/api/v1/entries/expense \
-H "X-Api-Key: YOUR_INVOICE_KEY" \
-d '{"description": "Test expense", "amount": "100.00 EUR", "account_name": "Expenses:Test"}'
# Check user balance
curl http://localhost:5000/castle/api/v1/balance \
curl http://localhost:5000/libra/api/v1/balance \
-H "X-Api-Key: YOUR_INVOICE_KEY"
```