{% extends "base.html" %} {% from "macros.jinja" import window_vars with context %} {% block scripts %} {{ window_vars(user) }} {% endblock %} {% block page %}
🏰 Castle Accounting

Track expenses, receivables, and balances for the collective

Configure Your Wallet Manage Permissions (Admin) Sync Accounts from Beancount Castle Settings (Super User Only)
Setup Required: Castle Wallet ID must be configured before the extension can function.
Setup Required: This extension requires configuration by the super user before it can be used.
Wallet Setup Required: You must configure your wallet before using this extension.
Pending Expense Approvals
Pending approval {% raw %}{{ entry.description }}{% endraw %} {% raw %}{{ formatDate(entry.entry_date) }}{% endraw %} User: {% raw %}{{ entry.username }}{% endraw %} Ref: {% raw %}{{ entry.reference }}{% endraw %} {% raw %}{{ formatSats(getTotalAmount(entry)) }} sats{% endraw %} {% raw %}{{ getEntryFiatAmount(entry) }}{% endraw %}
Approve Reject
Quick Actions
Add Expense Castle wallet must be configured first You must configure your wallet first Add Receivable Castle wallet must be configured first Record when a user owes the Castle View Transactions
Outstanding Balances by User
Your Balance
Refresh balance
{% raw %}{{ formatSats(Math.abs(balance.balance)) }} sats{% endraw %}
{% raw %}{{ formatFiat(Math.abs(amount), currency) }}{% endraw %}
{% raw %}{{ balance.balance > 0 ? 'Total owed to you' : balance.balance < 0 ? 'Total you owe' : 'No outstanding balances' }}{% endraw %}
{% raw %}{{ balance.balance >= 0 ? 'You owe Castle' : 'Castle owes you' }}{% endraw %}
Pay Balance Request Manual Payment
Loading balance...
Pending Manual Payment Requests
{% raw %}{{ request.description }}{% endraw %} User: {% raw %}{{ getUserName(request.user_id) }}{% endraw %} Requested: {% raw %}{{ formatDate(request.created_at) }}{% endraw %} {% raw %}{{ formatSats(request.amount) }} sats{% endraw %}
Approve Reject
Recent Transactions
Refresh transactions
Show transactions from:
From:
To:
No transactions yet
{% raw %}{{ transactionPagination.offset + 1 }} - {{ Math.min(transactionPagination.offset + transactionPagination.limit, transactionPagination.total) }} of {{ transactionPagination.total }}{% endraw %}
Balance Assertions
Write a balance assertion to Beancount ledger for automatic validation
{% raw %}{{ failedAssertions.length }}{% endraw %} Failed Assertion{% raw %}{{ failedAssertions.length > 1 ? 's' : '' }}{% endraw %}
Assertion failed Account: {% raw %}{{ getAccountName(assertion.account_id) }}{% endraw %} Expected: {% raw %}{{ formatSats(assertion.expected_balance_sats) }}{% endraw %} sats Actual: {% raw %}{{ formatSats(assertion.checked_balance_sats) }}{% endraw %} sats Difference: {% raw %}{{ formatSats(assertion.difference_sats) }}{% endraw %} sats Expected: {% raw %}{{ assertion.expected_balance_fiat }} {{ assertion.fiat_currency }}{% endraw %} | Actual: {% raw %}{{ assertion.checked_balance_fiat }} {{ assertion.fiat_currency }}{% endraw %} | Difference: {% raw %}{{ assertion.difference_fiat }} {{ assertion.fiat_currency }}{% endraw %} Checked: {% raw %}{{ formatDate(assertion.checked_at) }}{% endraw %}
Re-check assertion Delete assertion
Assertion passed {% raw %}{{ getAccountName(assertion.account_id) }}{% endraw %} Balance: {% raw %}{{ formatSats(assertion.checked_balance_sats) }}{% endraw %} sats Fiat: {% raw %}{{ assertion.checked_balance_fiat }} {{ assertion.fiat_currency }}{% endraw %} Checked: {% raw %}{{ formatDate(assertion.checked_at) }}{% endraw %}
Re-check assertion Delete assertion
No balance assertions yet. Create one to add checkpoints to your Beancount ledger and verify accounting accuracy.
Reconciliation Dashboard
Re-check all balance assertions
Balance Assertions
{% raw %}{{ reconciliation.summary.assertions.total }}{% endraw %}
{% raw %}{{ reconciliation.summary.assertions.passed }}{% endraw %} passed | {% raw %}{{ reconciliation.summary.assertions.failed }}{% endraw %} failed | {% raw %}{{ reconciliation.summary.assertions.pending }}{% endraw %} pending
Journal Entries
{% raw %}{{ reconciliation.summary.entries.total }}{% endraw %}
{% raw %}{{ reconciliation.summary.entries.cleared }}{% endraw %} cleared | {% raw %}{{ reconciliation.summary.entries.pending }}{% endraw %} pending | {% raw %}{{ reconciliation.summary.entries.flagged }}{% endraw %} flagged
Total Accounts
{% raw %}{{ reconciliation.summary.accounts.total }}{% endraw %}
Last checked: {% raw %}{{ reconciliation.summary.last_checked ? formatDate(reconciliation.summary.last_checked) : 'Never' }}{% endraw %}
{% raw %}{{ reconciliation.discrepancies.total_discrepancies }}{% endraw %} Discrepancy(ies) Found
{% raw %}{{ reconciliation.discrepancies.failed_assertions.length }}{% endraw %} failed assertions, {% raw %}{{ reconciliation.discrepancies.flagged_entries.length }}{% endraw %} flagged entries
Failed Assertions
{% raw %}{{ getAccountName(assertion.account_id) }}{% endraw %} Expected: {% raw %}{{ formatSats(assertion.expected_balance_sats) }}{% endraw %} | Actual: {% raw %}{{ formatSats(assertion.checked_balance_sats) }}{% endraw %} | Diff: {% raw %}{{ formatSats(assertion.difference_sats) }}{% endraw %}
Flagged Entries
{% raw %}{{ entry.description }}{% endraw %} {% raw %}{{ formatDate(entry.entry_date) }}{% endraw %}
All accounts reconciled successfully!
Chart of Accounts
{% raw %}{{ account.name }}{% endraw %} {% raw %}{{ account.account_type }}{% endraw %}
No accounts available
Add Expense
Submit Expense Cancel
Pay Balance
Amount owed: {% raw %}{{ formatSats(Math.abs(balance.balance)) }}{% endraw %} sats
{% raw %}{{ formatFiat(Math.abs(amount), currency) }}{% endraw %}
Generate Lightning Invoice Pay Manually (Cash/Bank)
Cancel
Scan the QR code or copy the invoice to pay with your Lightning wallet. Your balance will update automatically after payment.
Close
Request Manual Payment
Request the Castle to pay you manually (cash, bank transfer, etc.) to settle your balance.
Current balance: {% raw %}{{ formatSats(Math.abs(balance.balance)) }}{% endraw %} sats
{% raw %}{{ formatFiat(Math.abs(amount), currency) }}{% endraw %}
Submit Request Cancel
Castle Settings
Super User Only: Only the super user can modify these settings.
Select the wallet that will be used for Castle operations and transactions.
Fava/Beancount Integration
Save Settings {% raw %}{{ isSuperUser ? 'Cancel' : 'Close' }}{% endraw %}
Configure Your Wallet
Select the wallet you'll use for Castle transactions.
Save Wallet Cancel
Add Receivable
Submit Receivable Cancel
Create Balance Assertion
Balance assertions are written to your Beancount ledger and validated automatically by Beancount. This verifies that an account's actual balance matches your expected balance at a specific date. If the assertion fails, Beancount will alert you to investigate the discrepancy. Castle stores metadata (tolerance, notes) for your convenience.
Optional: Fiat Balance Check
Create & Check Cancel
Settle Receivable
User
{% raw %}{{ settleReceivableDialog.username }}{% endraw %}
{% raw %}{{ settleReceivableDialog.user_id }}{% endraw %}
Amount Owed
{% raw %}{{ formatSats(settleReceivableDialog.maxAmount) }}{% endraw %} sats
{% raw %}{{ formatFiat(settleReceivableDialog.maxAmountFiat, settleReceivableDialog.fiatCurrency) }}{% endraw %}
Lightning Invoice Generated
Waiting for payment...
Generate Invoice Settle Receivable Cancel
Pay User
User
{% raw %}{{ payUserDialog.username }}{% endraw %}
{% raw %}{{ payUserDialog.user_id }}{% endraw %}
Amount Castle Owes
{% raw %}{{ formatSats(payUserDialog.maxAmount) }}{% endraw %} sats
{% raw %}{{ formatFiat(payUserDialog.maxAmountFiat, payUserDialog.fiatCurrency) }}{% endraw %}
Payment sent successfully!
Send Lightning Payment Record Payment {% raw %}{{ payUserDialog.paymentSuccess ? 'Close' : 'Cancel' }}{% endraw %}
{% endblock %}