padreug commented on issue aiolabs/lightning-pub#12

padreug
2026-04-01T16:51:23Z

Extension: Castle (Accounting)
NIP Review: Applicable Standards for Castle Extension

Castle has the richest NIP surface area of the three extensions. The Nostr protocol provides building blocks for every major accounting concern: authorization, audit trails, encrypted communication, role management, and payment settlement.

Core NIPs to Implement

NIP-47: Nostr Wallet Connect — SETTLEMENT ENGINE

Kinds: 13194, 23194, 23195, 23197

Maps directly to accounting operations:

NWC Method | Accounting Use
make_invoice | Create receivables (user owes Castle)
pay_invoice | Settle payables (Castle pays user)
list_transactions | Authoritative transaction feed for ledger entries
get_balance | Cash position reconciliation
make_hold_invoice / settle_hold_invoice | Accrual accounting — escrow/pending states before settlement

Key insight: Hold invoices enable proper accrual accounting. Create hold invoice = record receivable; settle = recognize cash receipt. Cancel = write off. This maps directly to Castle's "pending → cleared" journal entry lifecycle.

The fees_paid field from responses directly populates the expense ledger for Lightning network costs — no manual fee tracking needed.

NIP-78: Application-Specific Data — CHART OF ACCOUNTS

Kind: 30078

Store the entire chart of accounts and Castle configuration as encrypted addressable events:

{
  "kind": 30078,
  "tags": [["d", "castle/chart-of-accounts"]],
  "content": "nip44_encrypt({
    'accounts': [
      {'code': '1000', 'name': 'Assets:Lightning:Balance', 'type': 'asset'},
      {'code': '2000', 'name': 'Liabilities:Payable:User-abc', 'type': 'liability'},
      {'code': '4000', 'name': 'Income:Accommodation', 'type': 'revenue'},
      {'code': '5000', 'name': 'Expenses:Food', 'type': 'expense'}
    ]
  })"
}

Also use separate d tags for settings (castle/settings), reporting period snapshots, and journal entry templates. Being addressable and replaceable means the chart evolves without event proliferation.

NIP-17: Private Direct Messages — APPROVAL WORKFLOWS

Kinds: 14 (chat), 15 (file), 1059 (gift wrap), 13 (seal)

This is how Castle replaces its web-only approval UI with a protocol-native workflow:

1. User submits expense → Extension creates sealed DM (kind 14 → 13 → 1059) to admin

2. Admin reviews → Decrypts, sees amount/vendor/account, replies with approval/rejection

3. On approval → Extension triggers NWC payment and records journal entry

4. File attachments → kind 15 for receipt images/PDFs with SHA-256 integrity hash

The three-layer structure (rumor → seal → gift wrap) means relays never see who is approving what — critical for financial privacy.

NIP-59: Gift Wrap — METADATA PROTECTION

Kinds: 13 (seal), 1059 (gift wrap)

Wraps all sensitive financial communications. Temporal obfuscation (randomized created_at up to 2 days) prevents correlation attacks like "payment arrived → 2 minutes later approval was sent." Each approval gets a unique ephemeral key.

NIP-58: Badges — ROLE-BASED ACCESS CONTROL

Kinds: 30009 (badge definition), 8 (badge award), 30008 (profile badges)

Replace Castle's database RBAC with Nostr-native roles:

// Define role
{ "kind": 30009, "tags": [["d", "castle-accountant"], ["name", "Accountant"], ["description", "Can record journal entries"]] }

// Award role
{ "kind": 8, "tags": [["a", "30009:<castle-pubkey>:castle-accountant"], ["p", "<user-pubkey>"]] }

Roles to define:

castle-admin — Full access (super user)

castle-accountant — Record entries, view balances

castle-approver — Approve expenses and payment requests

castle-viewer — Read-only balance and report access

Badges are non-transferable and cryptographically signed — audit-proof role assignment.

NIP-32: Labeling — TRANSACTION CLASSIFICATION

Kind: 1985

Tag journal entries with accounting metadata:

{
  "kind": 1985,
  "tags": [
    ["L", "castle.cost-center"],
    ["l", "kitchen", "castle.cost-center"],
    ["L", "castle.expense-type"],
    ["l", "supplies", "castle.expense-type"],
    ["e", "<journal-entry-event-id>"]
  ]
}

Enables slicing the ledger by cost center, department, vendor, project — all queryable via standard Nostr filters.

Supplementary NIPs

NIP | Kind | Use Case | Priority
NIP-44 (Encryption) | — | All encrypted data uses ChaCha20+HMAC; mandatory | Required
NIP-51 (Lists) | 30000 | Permission sets — which pubkeys can access which accounts | Recommended
NIP-57 (Zaps) | 9735 | Zap receipts as payment evidence for revenue entries | Recommended
NIP-56 (Reporting) | 1984 | Flag suspicious entries (fraud, duplicate, policy violation) | Optional
NIP-40 (Expiration) | — | Temporary permissions with auto-expiry | Optional
NIP-84 (Highlights) | 9802 | Auditor highlights material transactions for review | Nice-to-have
NIP-B7 (Blossom) | 10063 | Decentralized receipt/invoice PDF storage with SHA-256 verification | Future
NIP-EE (MLS Groups) | 443-445 | Scalable group encryption for large finance teams | Future

Double-Entry Architecture Using NIPs

Journal entry as a composite Nostr structure:

Journal entry header stored in extension DB (or as encrypted NIP-78 event)

Each entry line tagged with NIP-32 labels (cost center, account code)

Payment settlement via NIP-47 NWC

Approval chain via NIP-17 sealed DMs

Roles verified via NIP-58 badges

Audit queries via standard Nostr tag filters (#e, #p, #a, #l)

Settlement flow:

User submits expense (NIP-17 DM to admin)
  → Admin approves (NIP-17 reply, verified by NIP-58 badge)
    → Extension creates journal entry (DR Expense, CR Liability)
      → When Castle pays user: NWC pay_invoice (NIP-47)
        → Record settlement entry (DR Liability, CR Assets:Lightning)
          → Tag with NIP-32 labels for reporting

What replaces Fava/Beancount:

Balance calculations: SUM of entry lines grouped by account in SQLite (no external service)

Trial balance: Query all entry lines, verify debits = credits

Account hierarchy: : separator parsed in TypeScript, permission inheritance via string prefix matching

Cost basis tracking: Store fiat metadata in entry line fields, never convert from current sats rate

Summary

Castle benefits from the most NIPs because accounting touches identity (NIP-58), communication (NIP-17/59), payments (NIP-47/57), data storage (NIP-78), classification (NIP-32), and access control (NIP-51). The protocol provides every primitive needed — the extension's job is to enforce double-entry invariants and orchestrate the workflow.