docs: update functional identifier refs to spirekeeper
Some checks failed
ci.yml / docs: update functional identifier refs to spirekeeper (push) Failing after 0s
Some checks failed
ci.yml / docs: update functional identifier refs to spirekeeper (push) Failing after 0s
Runbook SQL (spirekeeper.dca_*), ext URL paths, code-location paths, and the DB-schema name in docs/CLAUDE/README move to the new identity. Rewrites the placeholder description.md with a real one. Historical aiolabs/satmachineadmin#N issue/repo links stay pointing at the original repo. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a059e3f596
commit
4ac640a499
6 changed files with 142 additions and 143 deletions
|
|
@ -213,7 +213,7 @@ commission_amount = 266800 - 258835 = 7,965 sats (to commission wallet)
|
|||
### Security Considerations
|
||||
- **Superuser Authentication**: Admin extension requires LNBits superuser login
|
||||
- **Wallet Admin Keys**: Client extension uses wallet admin keys for user operations
|
||||
- **Database Access**: Only superusers can write to satoshimachine database
|
||||
- **Database Access**: Only superusers can write to spirekeeper database
|
||||
- SSH tunnel encryption for database connectivity
|
||||
- Read-only database permissions for Lamassu access
|
||||
- Input sanitization and type validation
|
||||
|
|
@ -230,7 +230,7 @@ on existing installs:
|
|||
```sql
|
||||
SELECT a.id, a.username, a.pubkey, m.id, m.machine_npub
|
||||
FROM accounts a
|
||||
JOIN ext_satoshimachine.dca_machines m
|
||||
JOIN ext_spirekeeper.dca_machines m
|
||||
ON LOWER(a.pubkey) = LOWER(m.machine_npub);
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ The extension creates several tables:
|
|||
├── config.json # Extension configuration
|
||||
├── manifest.json # Extension manifest
|
||||
├── templates/
|
||||
│ └── satmachineadmin/
|
||||
│ └── spirekeeper/
|
||||
│ └── index.html # Main UI template
|
||||
└── static/
|
||||
└── js/
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
SatMachineAdmin can be used as a template for building new extensions, it includes a bunch of functions that can be edited/deleted as you need them.
|
||||
spirekeeper is the operator-side administration extension for bitSpire — it manages one or more bitSpire ATMs and runs the Dollar Cost Averaging (DCA) distribution that pays registered clients out of their confirmed deposit balances.
|
||||
|
||||
This is a longform description that will be used in the advanced description when users click on the "more" button on the extension cards.
|
||||
It listens for settlement events published by each ATM over Nostr (kind-21000, NIP-44 v2 encrypted), pays the resulting invoices through LNbits, and splits the proceeds across the principal, operator-fee, and commission legs according to the per-machine and super-config fee settings.
|
||||
|
||||
Adding some bullets is nice covering:
|
||||
- **ATM fleet management** — register and configure each bitSpire by its machine npub; publish fee config and cassette state over Nostr.
|
||||
- **DCA distribution** — proportional ("flow") allocation across clients based on their remaining deposit balances, with a full per-leg audit trail.
|
||||
- **Operator administration** — deposit confirmation workflow, client balance tracking, settlement history with drill-down, and CSV export for accounting.
|
||||
|
||||
- Functionality
|
||||
- Use cases
|
||||
|
||||
...and some other text about just how great this etension is.
|
||||
Requires LNbits superuser access for administration. The Lightning ATM side is served by the bitSpire device; this extension is the back office.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
## 0 · Why this document exists
|
||||
|
||||
Today the satoshi‑machine code lives at `~/dev/shared/extensions/satmachineadmin` on branch `v2-bitspire`. v2 swapped the legacy Lamassu SSH/PostgreSQL polling model for a Nostr‑native one: bitSpire publishes invoices over kind‑21000 NIP‑44 v2 events, LNbits pays them, and our extension hooks the resulting `Payment` object.
|
||||
Today the satoshi‑machine code lives at `~/dev/shared/extensions/spirekeeper` on branch `v2-bitspire`. v2 swapped the legacy Lamassu SSH/PostgreSQL polling model for a Nostr‑native one: bitSpire publishes invoices over kind‑21000 NIP‑44 v2 events, LNbits pays them, and our extension hooks the resulting `Payment` object.
|
||||
|
||||
The hard truth: the *settlement* itself uses Lightning (so it can't be forged once a preimage lands), but everything *around* the settlement — who the ATM is, what operator it belongs to, what the principal/commission split was, and what fiat was dispensed — currently rides on **mutable, unauthenticated metadata** (`Payment.extra`) plus a **stopgap that has the ATM hold the operator's own Nostr private key**. The latter means physical possession of the ATM = total compromise of the operator's LNbits account.
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ Lamassu's old answer here was TLS cert pinning. We have a richer toolbox — Nos
|
|||
│
|
||||
▼
|
||||
register_invoice_listener fires
|
||||
satmachineadmin/tasks.py:_handle_payment
|
||||
spirekeeper/tasks.py:_handle_payment
|
||||
│
|
||||
┌─────────────────────────────────┴────────────────────────────┐
|
||||
▼ ▼
|
||||
|
|
@ -144,7 +144,7 @@ T3, T5, T6 are the ones that keep the hardware honest. T3 + T6 are *the* reason
|
|||
|
||||
## 4 · Audit findings — current state inventory
|
||||
|
||||
Pulled from the two recent code‑level audits of `~/dev/shared/extensions/satmachineadmin` (operator‑scoping inventory) and `~/dev/lnbits/nostr-transport` (transport primitives).
|
||||
Pulled from the two recent code‑level audits of `~/dev/shared/extensions/spirekeeper` (operator‑scoping inventory) and `~/dev/lnbits/nostr-transport` (transport primitives).
|
||||
|
||||
### 4.1 What's already strong
|
||||
|
||||
|
|
@ -307,7 +307,7 @@ None of those need to change. The new layers slot in *above* them.
|
|||
| **S4 — NIP‑78 per‑machine config + fleet roster** | Operator publishes `kind:30078` config + `kind:30000` fleet list. Handler cross‑checks ATM npub ∈ fleet; reads max‑withdraw/fee policy from config. | G1, G9 | 1 week | Define config schema; backwards‑compat path for pre‑NIP‑78 machines. |
|
||||
| **S5 — `sender_pubkey` persistence + signed metadata in Payment.extra** | When the dispatcher writes a Payment row, it stamps `Payment.extra.sender_pubkey`, `delegation_root`, and an HMAC over the key fields keyed by the LNbits server's own secret. Mutation post‑write breaks the HMAC. | G2 (DB‑side), G5, G6 | 3–5 days | LNbits PR — fairly localised. |
|
||||
| **S6 — Rate limiting + roster‑gated auto‑account** | Auto‑account‑from‑npub only fires if the npub appears in some operator's NIP‑78 fleet OR if an explicit "open enrollment" flag is set. Relay/handler‑level rate limit per pubkey. | G8, G9 | 1 week | LNbits PR. |
|
||||
| **S7 — NIP‑46 bunker option** | Operator can pair satmachineadmin with a Bunker (Amber, Nunchuk Custody, etc.). Operator's nsec leaves LNbits' DB; LNbits stores only the bunker connection. | G6, partial G5 | 4–6 weeks | Largest. Defer until S0–S5 land. |
|
||||
| **S7 — NIP‑46 bunker option** | Operator can pair spirekeeper with a Bunker (Amber, Nunchuk Custody, etc.). Operator's nsec leaves LNbits' DB; LNbits stores only the bunker connection. | G6, partial G5 | 4–6 weeks | Largest. Defer until S0–S5 land. |
|
||||
| **S8 — Cash‑in path** | Wire `is_out=True` cash‑in handling: LNURL‑withdraw with expiration matching the kind‑21000 invoice TTL, attestation receipt on settle, refund queue for stale links. | G10 | 2 weeks | Out of scope for this security doc but tracked here for completeness. |
|
||||
|
||||
Recommended sequencing for the *next sprint*: **S0 + S1 + S5**. They give us the biggest security delta with no upstream LNbits dependency for S0/S1 and a small, well‑scoped LNbits patch for S5. S2/S3/S4 are the proper Nostr‑native layer and should land in the sprint after.
|
||||
|
|
@ -359,12 +359,12 @@ For an auditor or new contributor doing a walk‑through:
|
|||
|
||||
| File | Role | Note |
|
||||
|---|---|---|
|
||||
| `~/dev/shared/extensions/satmachineadmin/tasks.py` | LNbits invoice listener. Entry point for all settlements today. | `_handle_payment:56-95` — load‑bearing routing. |
|
||||
| `~/dev/shared/extensions/satmachineadmin/bitspire.py` | Parses Payment.extra. The trust boundary. | `parse_settlement:68-92` — happy vs fallback path. |
|
||||
| `~/dev/shared/extensions/satmachineadmin/distribution.py` | Three‑leg distribution chain. | `process_settlement` — uses claim pattern. |
|
||||
| `~/dev/shared/extensions/satmachineadmin/crud.py` | Operator‑scoped DB layer. | `claim_settlement_for_processing`, `_machine_owned_by`. |
|
||||
| `~/dev/shared/extensions/satmachineadmin/views_api.py` | 33 routes, all `check_user_exists` except super‑config PUT. | `_assert_wallet_owned_by` is the wallet‑IDOR fix. |
|
||||
| `~/dev/shared/extensions/satmachineadmin/migrations.py` | Schema. | `dca_settlements` is the audit row; `dca_payments` is the leg row. |
|
||||
| `~/dev/shared/extensions/spirekeeper/tasks.py` | LNbits invoice listener. Entry point for all settlements today. | `_handle_payment:56-95` — load‑bearing routing. |
|
||||
| `~/dev/shared/extensions/spirekeeper/bitspire.py` | Parses Payment.extra. The trust boundary. | `parse_settlement:68-92` — happy vs fallback path. |
|
||||
| `~/dev/shared/extensions/spirekeeper/distribution.py` | Three‑leg distribution chain. | `process_settlement` — uses claim pattern. |
|
||||
| `~/dev/shared/extensions/spirekeeper/crud.py` | Operator‑scoped DB layer. | `claim_settlement_for_processing`, `_machine_owned_by`. |
|
||||
| `~/dev/shared/extensions/spirekeeper/views_api.py` | 33 routes, all `check_user_exists` except super‑config PUT. | `_assert_wallet_owned_by` is the wallet‑IDOR fix. |
|
||||
| `~/dev/shared/extensions/spirekeeper/migrations.py` | Schema. | `dca_settlements` is the audit row; `dca_payments` is the leg row. |
|
||||
| `~/dev/shocknet/lamassu-next/deploy/nixos/provision-atm.sh` | Where keys land on the ATM today. | `:81-99` — `VITE_ATM_PRIVATE_KEY` and the Option‑1 stopgap. |
|
||||
| `~/dev/lnbits/nostr-transport/lnbits/core/services/nostr_transport/` | LNbits transport handler (upstream we depend on). | NIP‑44 v2 crypto here; G5/G6/G7 fixes will live here. |
|
||||
| `~/dev/nostr-protocol/nips/26.md` | Delegation. | Source for S2. |
|
||||
|
|
@ -397,7 +397,7 @@ How we'd test the proposed design end‑to‑end, once S0–S5 land:
|
|||
|
||||
Once approved:
|
||||
|
||||
1. The PDF for printing will be generated post‑plan‑mode (requires shell exec). Recommended path: render the markdown via `pandoc` to `~/dev/shared/extensions/satmachineadmin/docs/security-pathway-v1.pdf`; the markdown source will live at `~/dev/shared/extensions/satmachineadmin/docs/security-pathway-v1.md` so future contributors edit it in‑repo.
|
||||
1. The PDF for printing will be generated post‑plan‑mode (requires shell exec). Recommended path: render the markdown via `pandoc` to `~/dev/shared/extensions/spirekeeper/docs/security-pathway-v1.pdf`; the markdown source will live at `~/dev/shared/extensions/spirekeeper/docs/security-pathway-v1.md` so future contributors edit it in‑repo.
|
||||
2. Open Forgejo epics on `aiolabs/satmachineadmin` linking back to existing `#9/#11/#12` and adding a new one for "Security pathway hardening (S0–S7)."
|
||||
3. Open a tracking issue on `aiolabs/lnbits` against the `nostr-transport` branch for the LNbits‑side primitives (S2, S5, S6).
|
||||
4. Sequence sprint: **S0 + S1 + S5 first** (highest ratio of security delta to upstream coupling). S2/S3/S4 in the following sprint.
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
-- Find duplicate transactions
|
||||
SELECT transaction_id, COUNT(*) as count,
|
||||
STRING_AGG(id::text, ', ') as record_ids
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
GROUP BY transaction_id
|
||||
HAVING COUNT(*) > 1;
|
||||
```
|
||||
|
|
@ -60,16 +60,16 @@ HAVING COUNT(*) > 1;
|
|||
-- Step 1: Identify duplicate distributions
|
||||
SELECT lt.transaction_id, lt.id, lt.created_at, lt.base_amount,
|
||||
COUNT(dp.id) as distribution_count
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
LEFT JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
LEFT JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
GROUP BY lt.id
|
||||
HAVING COUNT(dp.id) > (SELECT COUNT(*) FROM satoshimachine.dca_clients WHERE remaining_balance > 0);
|
||||
HAVING COUNT(dp.id) > (SELECT COUNT(*) FROM spirekeeper.dca_clients WHERE remaining_balance > 0);
|
||||
|
||||
-- Step 2: Calculate over-distributed amounts per client
|
||||
SELECT client_id,
|
||||
SUM(amount_sats) as total_received,
|
||||
-- Manual calculation of expected amount needed here
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
WHERE lamassu_transaction_id IN (SELECT id FROM duplicates_table)
|
||||
GROUP BY client_id;
|
||||
```
|
||||
|
|
@ -94,7 +94,7 @@ if existing:
|
|||
|
||||
**Required Database Change**:
|
||||
```sql
|
||||
ALTER TABLE satoshimachine.lamassu_transactions
|
||||
ALTER TABLE spirekeeper.lamassu_transactions
|
||||
ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
||||
```
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
|||
-- Check last successful poll
|
||||
SELECT MAX(created_at) as last_transaction,
|
||||
EXTRACT(EPOCH FROM (NOW() - MAX(created_at)))/3600 as hours_since_last
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
|
||||
-- If hours_since_last > 24, investigate immediately
|
||||
```
|
||||
|
|
@ -194,8 +194,8 @@ ssh-keygen -t ed25519 -f ~/.ssh/satmachine_lamassu -C "satmachine-polling"
|
|||
-- Find stuck/failed payments (older than 1 hour, not completed)
|
||||
SELECT dp.id, dp.client_id, dp.amount_sats, dp.status, dp.created_at,
|
||||
c.username, dp.payment_hash
|
||||
FROM satoshimachine.dca_payments dp
|
||||
JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.dca_payments dp
|
||||
JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
WHERE dp.status != 'completed'
|
||||
AND dp.created_at < NOW() - INTERVAL '1 hour'
|
||||
ORDER BY dp.created_at DESC;
|
||||
|
|
@ -207,7 +207,7 @@ ORDER BY dp.created_at DESC;
|
|||
SELECT
|
||||
SUM(commission_amount) as total_commission_expected,
|
||||
-- Manually check actual wallet balance in LNBits
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
```
|
||||
|
||||
#### Immediate Response
|
||||
|
|
@ -258,9 +258,9 @@ SELECT
|
|||
c.remaining_balance,
|
||||
COALESCE(SUM(d.amount), 0) as total_deposits,
|
||||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_payments
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id;
|
||||
```
|
||||
|
||||
|
|
@ -319,9 +319,9 @@ SELECT
|
|||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_distributed,
|
||||
COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as calculated_balance,
|
||||
c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0)) as discrepancy
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id, c.username, c.remaining_balance
|
||||
HAVING ABS(c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0))) > 1;
|
||||
```
|
||||
|
|
@ -341,11 +341,11 @@ HAVING ABS(c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE
|
|||
```sql
|
||||
-- Get complete transaction history for client
|
||||
SELECT 'DEPOSIT' as type, id, amount, status, created_at, confirmed_at
|
||||
FROM satoshimachine.dca_deposits
|
||||
FROM spirekeeper.dca_deposits
|
||||
WHERE client_id = <client_id>
|
||||
UNION ALL
|
||||
SELECT 'PAYMENT' as type, id, amount_sats, status, created_at, NULL
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
WHERE client_id = <client_id>
|
||||
ORDER BY created_at;
|
||||
```
|
||||
|
|
@ -361,18 +361,18 @@ ORDER BY created_at;
|
|||
**Option A: Adjustment Entry** (Recommended)
|
||||
```sql
|
||||
-- Create compensating deposit for positive discrepancy
|
||||
INSERT INTO satoshimachine.dca_deposits (client_id, amount, status, note)
|
||||
INSERT INTO spirekeeper.dca_deposits (client_id, amount, status, note)
|
||||
VALUES (<client_id>, <adjustment_amount>, 'confirmed', 'Balance correction - reconciliation 2025-10-19');
|
||||
|
||||
-- OR Create compensating payment for negative discrepancy
|
||||
INSERT INTO satoshimachine.dca_payments (client_id, amount_sats, status, note)
|
||||
INSERT INTO spirekeeper.dca_payments (client_id, amount_sats, status, note)
|
||||
VALUES (<client_id>, <adjustment_amount>, 'completed', 'Balance correction - reconciliation 2025-10-19');
|
||||
```
|
||||
|
||||
**Option B: Direct Balance Update** (Use with extreme caution)
|
||||
```sql
|
||||
-- ONLY if audit trail is complete and discrepancy is unexplained
|
||||
UPDATE satoshimachine.dca_clients
|
||||
UPDATE spirekeeper.dca_clients
|
||||
SET remaining_balance = <correct_balance>,
|
||||
updated_at = NOW()
|
||||
WHERE id = <client_id>;
|
||||
|
|
@ -396,11 +396,11 @@ async def daily_reconciliation_check():
|
|||
**Database Constraints**:
|
||||
```sql
|
||||
-- Prevent negative balances
|
||||
ALTER TABLE satoshimachine.dca_clients
|
||||
ALTER TABLE spirekeeper.dca_clients
|
||||
ADD CONSTRAINT positive_balance CHECK (remaining_balance >= 0);
|
||||
|
||||
-- Prevent confirmed deposits with zero amount
|
||||
ALTER TABLE satoshimachine.dca_deposits
|
||||
ALTER TABLE spirekeeper.dca_deposits
|
||||
ADD CONSTRAINT positive_deposit CHECK (amount > 0);
|
||||
```
|
||||
|
||||
|
|
@ -435,7 +435,7 @@ SELECT
|
|||
-- Calculate differences
|
||||
base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as base_difference,
|
||||
commission_amount - ROUND(crypto_atoms - (crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100)))) as commission_difference
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
WHERE ABS(base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100)))) > 1
|
||||
OR ABS(commission_amount - ROUND(crypto_atoms - (crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))))) > 1;
|
||||
```
|
||||
|
|
@ -485,9 +485,9 @@ SELECT
|
|||
SUM(lt.base_amount) as total_distributed,
|
||||
SUM(expected_base) as should_have_distributed,
|
||||
SUM(expected_base - lt.base_amount) as client_impact
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
WHERE -- filter for affected transactions
|
||||
GROUP BY c.id;
|
||||
```
|
||||
|
|
@ -505,7 +505,7 @@ GROUP BY c.id;
|
|||
- Create compensating payments to affected clients:
|
||||
```sql
|
||||
-- Add to client balances
|
||||
UPDATE satoshimachine.dca_clients c
|
||||
UPDATE spirekeeper.dca_clients c
|
||||
SET remaining_balance = remaining_balance + adjustment.amount
|
||||
FROM (
|
||||
-- Calculate adjustment per client
|
||||
|
|
@ -572,7 +572,7 @@ assert abs(calculated_total - crypto_atoms) <= 1, "Commission calculation error
|
|||
SELECT
|
||||
SUM(commission_amount) as expected_total_commission,
|
||||
-- Compare to actual wallet balance in LNBits dashboard
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
WHERE created_at > '<date-of-last-known-good-balance>';
|
||||
```
|
||||
|
||||
|
|
@ -607,7 +607,7 @@ curl -X GET https://<lnbits-host>/api/v1/wallet \
|
|||
SELECT commission_wallet_id,
|
||||
LEFT(commission_wallet_adminkey, 10) || '...' as key_preview,
|
||||
updated_at
|
||||
FROM satoshimachine.lamassu_config
|
||||
FROM spirekeeper.lamassu_config
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1;
|
||||
```
|
||||
|
|
@ -783,9 +783,9 @@ WITH client_financials AS (
|
|||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_payments,
|
||||
COUNT(DISTINCT d.id) as deposit_count,
|
||||
COUNT(DISTINCT p.id) as payment_count
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id
|
||||
)
|
||||
SELECT
|
||||
|
|
@ -813,7 +813,7 @@ SELECT
|
|||
SUM(commission_amount) as total_commission,
|
||||
MIN(created_at) as first_transaction,
|
||||
MAX(created_at) as last_transaction
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
|
||||
-- 3. Failed/Pending Payments Check
|
||||
SELECT
|
||||
|
|
@ -822,7 +822,7 @@ SELECT
|
|||
SUM(amount_sats) as total_amount,
|
||||
MIN(created_at) as oldest,
|
||||
MAX(created_at) as newest
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
GROUP BY status
|
||||
ORDER BY
|
||||
CASE status
|
||||
|
|
@ -839,7 +839,7 @@ SELECT
|
|||
status,
|
||||
created_at,
|
||||
EXTRACT(EPOCH FROM (NOW() - created_at))/3600 as hours_pending
|
||||
FROM satoshimachine.dca_deposits
|
||||
FROM spirekeeper.dca_deposits
|
||||
WHERE status = 'pending'
|
||||
AND created_at < NOW() - INTERVAL '48 hours'
|
||||
ORDER BY created_at;
|
||||
|
|
@ -855,7 +855,7 @@ SELECT
|
|||
commission_amount,
|
||||
ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as expected_base,
|
||||
base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as difference
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
|
@ -876,15 +876,15 @@ psql -h localhost -U lnbits -d lnbits
|
|||
2. **Direct Configuration Update**:
|
||||
```sql
|
||||
-- Update Lamassu config directly
|
||||
UPDATE satoshimachine.lamassu_config
|
||||
UPDATE spirekeeper.lamassu_config
|
||||
SET polling_enabled = false
|
||||
WHERE id = (SELECT MAX(id) FROM satoshimachine.lamassu_config);
|
||||
WHERE id = (SELECT MAX(id) FROM spirekeeper.lamassu_config);
|
||||
```
|
||||
|
||||
3. **Manual Client Balance Update**:
|
||||
```sql
|
||||
-- ONLY in emergency when dashboard unavailable
|
||||
UPDATE satoshimachine.dca_clients
|
||||
UPDATE spirekeeper.dca_clients
|
||||
SET remaining_balance = <correct_amount>
|
||||
WHERE id = <client_id>;
|
||||
-- MUST document this action in incident log
|
||||
|
|
@ -910,19 +910,19 @@ uv run lnbits
|
|||
```bash
|
||||
# Export all DCA-related tables to CSV
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.lamassu_transactions;" \
|
||||
"SELECT * FROM spirekeeper.lamassu_transactions;" \
|
||||
> lamassu_transactions_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_payments;" \
|
||||
"SELECT * FROM spirekeeper.dca_payments;" \
|
||||
> dca_payments_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_deposits;" \
|
||||
"SELECT * FROM spirekeeper.dca_deposits;" \
|
||||
> dca_deposits_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_clients;" \
|
||||
"SELECT * FROM spirekeeper.dca_clients;" \
|
||||
> dca_clients_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
```
|
||||
|
||||
|
|
@ -946,9 +946,9 @@ SELECT
|
|||
dp.amount_sats as client_received,
|
||||
dp.status as payment_status,
|
||||
dp.payment_hash
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
LEFT JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
LEFT JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
LEFT JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
LEFT JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
ORDER BY lt.created_at DESC, c.username;
|
||||
```
|
||||
|
||||
|
|
@ -981,18 +981,18 @@ async def process_lamassu_transaction(txn_data: dict) -> Optional[LamassuTransac
|
|||
|
||||
```sql
|
||||
-- Add unique constraint on transaction_id
|
||||
ALTER TABLE satoshimachine.lamassu_transactions
|
||||
ALTER TABLE spirekeeper.lamassu_transactions
|
||||
ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
||||
|
||||
-- Prevent negative balances
|
||||
ALTER TABLE satoshimachine.dca_clients
|
||||
ALTER TABLE spirekeeper.dca_clients
|
||||
ADD CONSTRAINT positive_balance CHECK (remaining_balance >= 0);
|
||||
|
||||
-- Ensure positive amounts
|
||||
ALTER TABLE satoshimachine.dca_deposits
|
||||
ALTER TABLE spirekeeper.dca_deposits
|
||||
ADD CONSTRAINT positive_deposit CHECK (amount > 0);
|
||||
|
||||
ALTER TABLE satoshimachine.dca_payments
|
||||
ALTER TABLE spirekeeper.dca_payments
|
||||
ADD CONSTRAINT positive_payment CHECK (amount_sats > 0);
|
||||
```
|
||||
|
||||
|
|
@ -1196,7 +1196,7 @@ grep "wallet.*api\|payment_hash" lnbits.log | tail -50
|
|||
### System Access
|
||||
|
||||
**LNBits Admin Dashboard**:
|
||||
- URL: `https://<your-lnbits-host>/satoshimachine`
|
||||
- URL: `https://<your-lnbits-host>/spirekeeper`
|
||||
- Requires superuser authentication
|
||||
|
||||
**Database Access**:
|
||||
|
|
@ -1205,7 +1205,7 @@ grep "wallet.*api\|payment_hash" lnbits.log | tail -50
|
|||
sqlite3 /home/padreug/AioLabs/Git/lnbits-extensions/lnbits/data/database.sqlite
|
||||
|
||||
# Direct table access
|
||||
sqlite3 /path/to/db "SELECT * FROM satoshimachine.<table_name>;"
|
||||
sqlite3 /path/to/db "SELECT * FROM spirekeeper.<table_name>;"
|
||||
```
|
||||
|
||||
**Log Files**:
|
||||
|
|
@ -1274,7 +1274,7 @@ pkill -f lnbits
|
|||
|
||||
```sql
|
||||
-- Disable automatic polling
|
||||
UPDATE satoshimachine.lamassu_config
|
||||
UPDATE spirekeeper.lamassu_config
|
||||
SET polling_enabled = false;
|
||||
```
|
||||
|
||||
|
|
@ -1283,7 +1283,7 @@ SET polling_enabled = false;
|
|||
```sql
|
||||
-- All client balances summary
|
||||
SELECT id, username, remaining_balance, created_at
|
||||
FROM satoshimachine.dca_clients
|
||||
FROM spirekeeper.dca_clients
|
||||
ORDER BY remaining_balance DESC;
|
||||
```
|
||||
|
||||
|
|
@ -1291,7 +1291,7 @@ ORDER BY remaining_balance DESC;
|
|||
|
||||
```sql
|
||||
SELECT id, transaction_id, created_at, crypto_atoms, base_amount, commission_amount
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
|
@ -1299,7 +1299,7 @@ LIMIT 10;
|
|||
### Failed Payments
|
||||
|
||||
```sql
|
||||
SELECT * FROM satoshimachine.dca_payments
|
||||
SELECT * FROM spirekeeper.dca_payments
|
||||
WHERE status != 'completed'
|
||||
ORDER BY created_at DESC;
|
||||
```
|
||||
|
|
@ -1314,19 +1314,19 @@ BACKUP_DIR="emergency_backup_${DATE}"
|
|||
mkdir -p $BACKUP_DIR
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.lamassu_transactions;" \
|
||||
"SELECT * FROM spirekeeper.lamassu_transactions;" \
|
||||
> ${BACKUP_DIR}/lamassu_transactions.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_payments;" \
|
||||
"SELECT * FROM spirekeeper.dca_payments;" \
|
||||
> ${BACKUP_DIR}/dca_payments.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_deposits;" \
|
||||
"SELECT * FROM spirekeeper.dca_deposits;" \
|
||||
> ${BACKUP_DIR}/dca_deposits.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_clients;" \
|
||||
"SELECT * FROM spirekeeper.dca_clients;" \
|
||||
> ${BACKUP_DIR}/dca_clients.csv
|
||||
|
||||
echo "Backup complete in ${BACKUP_DIR}/"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
-- Find duplicate transactions
|
||||
SELECT transaction_id, COUNT(*) as count,
|
||||
STRING_AGG(id::text, ', ') as record_ids
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
GROUP BY transaction_id
|
||||
HAVING COUNT(*) > 1;
|
||||
```
|
||||
|
|
@ -60,16 +60,16 @@ HAVING COUNT(*) > 1;
|
|||
-- Step 1: Identify duplicate distributions
|
||||
SELECT lt.transaction_id, lt.id, lt.created_at, lt.base_amount,
|
||||
COUNT(dp.id) as distribution_count
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
LEFT JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
LEFT JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
GROUP BY lt.id
|
||||
HAVING COUNT(dp.id) > (SELECT COUNT(*) FROM satoshimachine.dca_clients WHERE remaining_balance > 0);
|
||||
HAVING COUNT(dp.id) > (SELECT COUNT(*) FROM spirekeeper.dca_clients WHERE remaining_balance > 0);
|
||||
|
||||
-- Step 2: Calculate over-distributed amounts per client
|
||||
SELECT client_id,
|
||||
SUM(amount_sats) as total_received,
|
||||
-- Manual calculation of expected amount needed here
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
WHERE lamassu_transaction_id IN (SELECT id FROM duplicates_table)
|
||||
GROUP BY client_id;
|
||||
```
|
||||
|
|
@ -94,7 +94,7 @@ if existing:
|
|||
|
||||
**Required Database Change**:
|
||||
```sql
|
||||
ALTER TABLE satoshimachine.lamassu_transactions
|
||||
ALTER TABLE spirekeeper.lamassu_transactions
|
||||
ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
||||
```
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
|||
-- Check last successful poll
|
||||
SELECT MAX(created_at) as last_transaction,
|
||||
EXTRACT(EPOCH FROM (NOW() - MAX(created_at)))/3600 as hours_since_last
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
|
||||
-- If hours_since_last > 24, investigate immediately
|
||||
```
|
||||
|
|
@ -194,8 +194,8 @@ ssh-keygen -t ed25519 -f ~/.ssh/satmachine_lamassu -C "satmachine-polling"
|
|||
-- Find stuck/failed payments (older than 1 hour, not completed)
|
||||
SELECT dp.id, dp.client_id, dp.amount_sats, dp.status, dp.created_at,
|
||||
c.username, dp.payment_hash
|
||||
FROM satoshimachine.dca_payments dp
|
||||
JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.dca_payments dp
|
||||
JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
WHERE dp.status != 'completed'
|
||||
AND dp.created_at < NOW() - INTERVAL '1 hour'
|
||||
ORDER BY dp.created_at DESC;
|
||||
|
|
@ -207,7 +207,7 @@ ORDER BY dp.created_at DESC;
|
|||
SELECT
|
||||
SUM(commission_amount) as total_commission_expected,
|
||||
-- Manually check actual wallet balance in LNBits
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
```
|
||||
|
||||
#### Immediate Response
|
||||
|
|
@ -258,9 +258,9 @@ SELECT
|
|||
c.remaining_balance,
|
||||
COALESCE(SUM(d.amount), 0) as total_deposits,
|
||||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_payments
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id;
|
||||
```
|
||||
|
||||
|
|
@ -319,9 +319,9 @@ SELECT
|
|||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_distributed,
|
||||
COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as calculated_balance,
|
||||
c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0)) as discrepancy
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id, c.username, c.remaining_balance
|
||||
HAVING ABS(c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0))) > 1;
|
||||
```
|
||||
|
|
@ -341,11 +341,11 @@ HAVING ABS(c.remaining_balance - (COALESCE(SUM(d.amount), 0) - COALESCE(SUM(CASE
|
|||
```sql
|
||||
-- Get complete transaction history for client
|
||||
SELECT 'DEPOSIT' as type, id, amount, status, created_at, confirmed_at
|
||||
FROM satoshimachine.dca_deposits
|
||||
FROM spirekeeper.dca_deposits
|
||||
WHERE client_id = <client_id>
|
||||
UNION ALL
|
||||
SELECT 'PAYMENT' as type, id, amount_sats, status, created_at, NULL
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
WHERE client_id = <client_id>
|
||||
ORDER BY created_at;
|
||||
```
|
||||
|
|
@ -361,18 +361,18 @@ ORDER BY created_at;
|
|||
**Option A: Adjustment Entry** (Recommended)
|
||||
```sql
|
||||
-- Create compensating deposit for positive discrepancy
|
||||
INSERT INTO satoshimachine.dca_deposits (client_id, amount, status, note)
|
||||
INSERT INTO spirekeeper.dca_deposits (client_id, amount, status, note)
|
||||
VALUES (<client_id>, <adjustment_amount>, 'confirmed', 'Balance correction - reconciliation 2025-10-19');
|
||||
|
||||
-- OR Create compensating payment for negative discrepancy
|
||||
INSERT INTO satoshimachine.dca_payments (client_id, amount_sats, status, note)
|
||||
INSERT INTO spirekeeper.dca_payments (client_id, amount_sats, status, note)
|
||||
VALUES (<client_id>, <adjustment_amount>, 'completed', 'Balance correction - reconciliation 2025-10-19');
|
||||
```
|
||||
|
||||
**Option B: Direct Balance Update** (Use with extreme caution)
|
||||
```sql
|
||||
-- ONLY if audit trail is complete and discrepancy is unexplained
|
||||
UPDATE satoshimachine.dca_clients
|
||||
UPDATE spirekeeper.dca_clients
|
||||
SET remaining_balance = <correct_balance>,
|
||||
updated_at = NOW()
|
||||
WHERE id = <client_id>;
|
||||
|
|
@ -396,11 +396,11 @@ async def daily_reconciliation_check():
|
|||
**Database Constraints**:
|
||||
```sql
|
||||
-- Prevent negative balances
|
||||
ALTER TABLE satoshimachine.dca_clients
|
||||
ALTER TABLE spirekeeper.dca_clients
|
||||
ADD CONSTRAINT positive_balance CHECK (remaining_balance >= 0);
|
||||
|
||||
-- Prevent confirmed deposits with zero amount
|
||||
ALTER TABLE satoshimachine.dca_deposits
|
||||
ALTER TABLE spirekeeper.dca_deposits
|
||||
ADD CONSTRAINT positive_deposit CHECK (amount > 0);
|
||||
```
|
||||
|
||||
|
|
@ -435,7 +435,7 @@ SELECT
|
|||
-- Calculate differences
|
||||
base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as base_difference,
|
||||
commission_amount - ROUND(crypto_atoms - (crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100)))) as commission_difference
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
WHERE ABS(base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100)))) > 1
|
||||
OR ABS(commission_amount - ROUND(crypto_atoms - (crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))))) > 1;
|
||||
```
|
||||
|
|
@ -485,9 +485,9 @@ SELECT
|
|||
SUM(lt.base_amount) as total_distributed,
|
||||
SUM(expected_base) as should_have_distributed,
|
||||
SUM(expected_base - lt.base_amount) as client_impact
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
WHERE -- filter for affected transactions
|
||||
GROUP BY c.id;
|
||||
```
|
||||
|
|
@ -505,7 +505,7 @@ GROUP BY c.id;
|
|||
- Create compensating payments to affected clients:
|
||||
```sql
|
||||
-- Add to client balances
|
||||
UPDATE satoshimachine.dca_clients c
|
||||
UPDATE spirekeeper.dca_clients c
|
||||
SET remaining_balance = remaining_balance + adjustment.amount
|
||||
FROM (
|
||||
-- Calculate adjustment per client
|
||||
|
|
@ -572,7 +572,7 @@ assert abs(calculated_total - crypto_atoms) <= 1, "Commission calculation error
|
|||
SELECT
|
||||
SUM(commission_amount) as expected_total_commission,
|
||||
-- Compare to actual wallet balance in LNBits dashboard
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
WHERE created_at > '<date-of-last-known-good-balance>';
|
||||
```
|
||||
|
||||
|
|
@ -607,7 +607,7 @@ curl -X GET https://<lnbits-host>/api/v1/wallet \
|
|||
SELECT commission_wallet_id,
|
||||
LEFT(commission_wallet_adminkey, 10) || '...' as key_preview,
|
||||
updated_at
|
||||
FROM satoshimachine.lamassu_config
|
||||
FROM spirekeeper.lamassu_config
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1;
|
||||
```
|
||||
|
|
@ -783,9 +783,9 @@ WITH client_financials AS (
|
|||
COALESCE(SUM(CASE WHEN p.status = 'completed' THEN p.amount_sats ELSE 0 END), 0) as total_payments,
|
||||
COUNT(DISTINCT d.id) as deposit_count,
|
||||
COUNT(DISTINCT p.id) as payment_count
|
||||
FROM satoshimachine.dca_clients c
|
||||
LEFT JOIN satoshimachine.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN satoshimachine.dca_payments p ON c.id = p.client_id
|
||||
FROM spirekeeper.dca_clients c
|
||||
LEFT JOIN spirekeeper.dca_deposits d ON c.id = d.client_id AND d.status = 'confirmed'
|
||||
LEFT JOIN spirekeeper.dca_payments p ON c.id = p.client_id
|
||||
GROUP BY c.id
|
||||
)
|
||||
SELECT
|
||||
|
|
@ -813,7 +813,7 @@ SELECT
|
|||
SUM(commission_amount) as total_commission,
|
||||
MIN(created_at) as first_transaction,
|
||||
MAX(created_at) as last_transaction
|
||||
FROM satoshimachine.lamassu_transactions;
|
||||
FROM spirekeeper.lamassu_transactions;
|
||||
|
||||
-- 3. Failed/Pending Payments Check
|
||||
SELECT
|
||||
|
|
@ -822,7 +822,7 @@ SELECT
|
|||
SUM(amount_sats) as total_amount,
|
||||
MIN(created_at) as oldest,
|
||||
MAX(created_at) as newest
|
||||
FROM satoshimachine.dca_payments
|
||||
FROM spirekeeper.dca_payments
|
||||
GROUP BY status
|
||||
ORDER BY
|
||||
CASE status
|
||||
|
|
@ -839,7 +839,7 @@ SELECT
|
|||
status,
|
||||
created_at,
|
||||
EXTRACT(EPOCH FROM (NOW() - created_at))/3600 as hours_pending
|
||||
FROM satoshimachine.dca_deposits
|
||||
FROM spirekeeper.dca_deposits
|
||||
WHERE status = 'pending'
|
||||
AND created_at < NOW() - INTERVAL '48 hours'
|
||||
ORDER BY created_at;
|
||||
|
|
@ -855,7 +855,7 @@ SELECT
|
|||
commission_amount,
|
||||
ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as expected_base,
|
||||
base_amount - ROUND(crypto_atoms / (1 + (commission_percentage * (100 - discount) / 100))) as difference
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
|
@ -876,15 +876,15 @@ psql -h localhost -U lnbits -d lnbits
|
|||
2. **Direct Configuration Update**:
|
||||
```sql
|
||||
-- Update Lamassu config directly
|
||||
UPDATE satoshimachine.lamassu_config
|
||||
UPDATE spirekeeper.lamassu_config
|
||||
SET polling_enabled = false
|
||||
WHERE id = (SELECT MAX(id) FROM satoshimachine.lamassu_config);
|
||||
WHERE id = (SELECT MAX(id) FROM spirekeeper.lamassu_config);
|
||||
```
|
||||
|
||||
3. **Manual Client Balance Update**:
|
||||
```sql
|
||||
-- ONLY in emergency when dashboard unavailable
|
||||
UPDATE satoshimachine.dca_clients
|
||||
UPDATE spirekeeper.dca_clients
|
||||
SET remaining_balance = <correct_amount>
|
||||
WHERE id = <client_id>;
|
||||
-- MUST document this action in incident log
|
||||
|
|
@ -910,19 +910,19 @@ uv run lnbits
|
|||
```bash
|
||||
# Export all DCA-related tables to CSV
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.lamassu_transactions;" \
|
||||
"SELECT * FROM spirekeeper.lamassu_transactions;" \
|
||||
> lamassu_transactions_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_payments;" \
|
||||
"SELECT * FROM spirekeeper.dca_payments;" \
|
||||
> dca_payments_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_deposits;" \
|
||||
"SELECT * FROM spirekeeper.dca_deposits;" \
|
||||
> dca_deposits_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
|
||||
sqlite3 -header -csv /path/to/lnbits/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_clients;" \
|
||||
"SELECT * FROM spirekeeper.dca_clients;" \
|
||||
> dca_clients_export_$(date +%Y%m%d_%H%M%S).csv
|
||||
```
|
||||
|
||||
|
|
@ -946,9 +946,9 @@ SELECT
|
|||
dp.amount_sats as client_received,
|
||||
dp.status as payment_status,
|
||||
dp.payment_hash
|
||||
FROM satoshimachine.lamassu_transactions lt
|
||||
LEFT JOIN satoshimachine.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
LEFT JOIN satoshimachine.dca_clients c ON dp.client_id = c.id
|
||||
FROM spirekeeper.lamassu_transactions lt
|
||||
LEFT JOIN spirekeeper.dca_payments dp ON dp.lamassu_transaction_id = lt.id
|
||||
LEFT JOIN spirekeeper.dca_clients c ON dp.client_id = c.id
|
||||
ORDER BY lt.created_at DESC, c.username;
|
||||
```
|
||||
|
||||
|
|
@ -981,18 +981,18 @@ async def process_lamassu_transaction(txn_data: dict) -> Optional[LamassuTransac
|
|||
|
||||
```sql
|
||||
-- Add unique constraint on transaction_id
|
||||
ALTER TABLE satoshimachine.lamassu_transactions
|
||||
ALTER TABLE spirekeeper.lamassu_transactions
|
||||
ADD CONSTRAINT unique_transaction_id UNIQUE (transaction_id);
|
||||
|
||||
-- Prevent negative balances
|
||||
ALTER TABLE satoshimachine.dca_clients
|
||||
ALTER TABLE spirekeeper.dca_clients
|
||||
ADD CONSTRAINT positive_balance CHECK (remaining_balance >= 0);
|
||||
|
||||
-- Ensure positive amounts
|
||||
ALTER TABLE satoshimachine.dca_deposits
|
||||
ALTER TABLE spirekeeper.dca_deposits
|
||||
ADD CONSTRAINT positive_deposit CHECK (amount > 0);
|
||||
|
||||
ALTER TABLE satoshimachine.dca_payments
|
||||
ALTER TABLE spirekeeper.dca_payments
|
||||
ADD CONSTRAINT positive_payment CHECK (amount_sats > 0);
|
||||
```
|
||||
|
||||
|
|
@ -1196,7 +1196,7 @@ grep "wallet.*api\|payment_hash" lnbits.log | tail -50
|
|||
### System Access
|
||||
|
||||
**LNBits Admin Dashboard**:
|
||||
- URL: `https://<your-lnbits-host>/satoshimachine`
|
||||
- URL: `https://<your-lnbits-host>/spirekeeper`
|
||||
- Requires superuser authentication
|
||||
|
||||
**Database Access**:
|
||||
|
|
@ -1205,7 +1205,7 @@ grep "wallet.*api\|payment_hash" lnbits.log | tail -50
|
|||
sqlite3 /home/padreug/AioLabs/Git/lnbits-extensions/lnbits/data/database.sqlite
|
||||
|
||||
# Direct table access
|
||||
sqlite3 /path/to/db "SELECT * FROM satoshimachine.<table_name>;"
|
||||
sqlite3 /path/to/db "SELECT * FROM spirekeeper.<table_name>;"
|
||||
```
|
||||
|
||||
**Log Files**:
|
||||
|
|
@ -1274,7 +1274,7 @@ pkill -f lnbits
|
|||
|
||||
```sql
|
||||
-- Disable automatic polling
|
||||
UPDATE satoshimachine.lamassu_config
|
||||
UPDATE spirekeeper.lamassu_config
|
||||
SET polling_enabled = false;
|
||||
```
|
||||
|
||||
|
|
@ -1283,7 +1283,7 @@ SET polling_enabled = false;
|
|||
```sql
|
||||
-- All client balances summary
|
||||
SELECT id, username, remaining_balance, created_at
|
||||
FROM satoshimachine.dca_clients
|
||||
FROM spirekeeper.dca_clients
|
||||
ORDER BY remaining_balance DESC;
|
||||
```
|
||||
|
||||
|
|
@ -1291,7 +1291,7 @@ ORDER BY remaining_balance DESC;
|
|||
|
||||
```sql
|
||||
SELECT id, transaction_id, created_at, crypto_atoms, base_amount, commission_amount
|
||||
FROM satoshimachine.lamassu_transactions
|
||||
FROM spirekeeper.lamassu_transactions
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 10;
|
||||
```
|
||||
|
|
@ -1299,7 +1299,7 @@ LIMIT 10;
|
|||
### Failed Payments
|
||||
|
||||
```sql
|
||||
SELECT * FROM satoshimachine.dca_payments
|
||||
SELECT * FROM spirekeeper.dca_payments
|
||||
WHERE status != 'completed'
|
||||
ORDER BY created_at DESC;
|
||||
```
|
||||
|
|
@ -1314,19 +1314,19 @@ BACKUP_DIR="emergency_backup_${DATE}"
|
|||
mkdir -p $BACKUP_DIR
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.lamassu_transactions;" \
|
||||
"SELECT * FROM spirekeeper.lamassu_transactions;" \
|
||||
> ${BACKUP_DIR}/lamassu_transactions.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_payments;" \
|
||||
"SELECT * FROM spirekeeper.dca_payments;" \
|
||||
> ${BACKUP_DIR}/dca_payments.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_deposits;" \
|
||||
"SELECT * FROM spirekeeper.dca_deposits;" \
|
||||
> ${BACKUP_DIR}/dca_deposits.csv
|
||||
|
||||
sqlite3 -header -csv /path/to/database.sqlite \
|
||||
"SELECT * FROM satoshimachine.dca_clients;" \
|
||||
"SELECT * FROM spirekeeper.dca_clients;" \
|
||||
> ${BACKUP_DIR}/dca_clients.csv
|
||||
|
||||
echo "Backup complete in ${BACKUP_DIR}/"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue