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:
parent
9c577c740c
commit
c174cda48d
44 changed files with 953 additions and 953 deletions
78
CLAUDE.md
78
CLAUDE.md
|
|
@ -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"
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue