49 lines
8.1 KiB
Markdown
Executable File
49 lines
8.1 KiB
Markdown
Executable File
# Custodial business-liability reconciliation
|
|
|
|
Status: implemented PostgreSQL **sandbox** integrity checks; not external proof of Monero backing or production settlement acceptance. Related: [production plan](production-plan.md), [financial storage](postgres-financial-storage.md). Updated 2026-10-20.
|
|
|
|
## Why totals are insufficient
|
|
|
|
A balanced journal and matching account projections can coexist with a corrupted order: its buyer, amount, status or pending task may disagree with the funds actually reserved. Aggregate assets equalling liabilities alone cannot detect a substituted recipient or orphan task.
|
|
|
|
[Business integrity checks](../server/market/custody-integrity.ts) now validate both directions:
|
|
|
|
- Strict persisted listing/order/withdrawal encodings, indexed identity/status columns and JSON record agreement.
|
|
- Order card, seller, buyer, sequence, price and historical hosting recipient against the original listing; exact independently rounded seller/author/host allocation and fixed author policy. Current operator fee configuration never rewrites history.
|
|
- Each awaiting-lock order has its exact credit-normal purchase reservation; cancelled/fixture-settled orders have zero reservation. The actual reservation and terminal journal lines must match the expected lifecycle, accounts and amounts, not just net to zero.
|
|
- Each withdrawal hold equals recipient amount plus maximum approved fee. Prepared/complete fixture records require an attempt ID, unique transaction identity and fee within cap. Cancelled/complete holds are zero; terminal postings and returned unused fee allowance match exactly.
|
|
- Blocked/cancelled/prepared/completed outbox state and payload correspond to the business record. Imported SQLite fixture terminal evidence is recognized explicitly; no real NFT/Monero settlement is inferred.
|
|
- Reverse checks reject orphan reservation accounts, pending work and lifecycle postings, duplicate listing orders, and reused prepared attempt/transaction identities.
|
|
|
|
Reads use the caller's coherent transaction. Record batches use UUID keyset pagination, maximum 128 per batch, with no growing process-wide customer cache. A 15-second business-scan budget is checked between batches/queries; the existing per-query timeout can extend the final boundary. This bounds application memory and failure behavior, **not a production capacity claim**. Very large histories may require indexed normalized schemas, incremental auditing and a dedicated database connection/worker budget before release.
|
|
|
|
The existing exact journal/projection/backing audit remains, and now invokes these checks at startup, explicit audit, SQLite import validation and deployment checks. Targeted pre/post checks run inside order/withdrawal mutation transactions. No database schema version or old financial record is rewritten by this increment.
|
|
|
|
## Runtime scheduling and failure behavior
|
|
|
|
The PostgreSQL backend schedules one nonoverlapping audit every 60 seconds. Mutations are held if the latest audit failed or the last success is over 120 seconds old. A transient availability failure can recover after a successful later audit; it does not automatically classify customer behavior as fraud.
|
|
|
|
A confirmed integrity mismatch latches the detecting process and stores a redacted `integrity_incident_v1` record in custody metadata. Subsequent writers check that marker under the existing cross-process advisory lock. Startup refuses it. Private activity and original signed receipt replay remain available; no new posting, refund, cancellation or withdrawal reservation is allowed while integrity is uncertain. This is deliberately stricter than an ordinary market-disabled flag, which still permits normal resolution.
|
|
|
|
For a targeted write-path mismatch, a savepoint rolls back business work while retaining the writer lock; the incident is committed before another cooperating writer can proceed. A background/read-only audit or startup failure records its incident separately. **Process crash or database unavailability can prevent that marker from committing.** A redacted error explicitly requests fencing all writers if marker persistence fails. A periodic audit is not an atomic fraud-prevention barrier for every unrelated write, nor protection from a privileged operator or fully compromised application.
|
|
|
|
Private loopback operations now expose only backend, mutation/incident hold, audit timestamps, freshness and a stable failure code. No customer ID, order ID, address, SQL statement, balance or connection secret is attached. [Operations checks](../server/operations-check.ts) emit `custody-financial-review` and `custody-audit-stale` warnings. No new public admin or financial audit endpoint was added.
|
|
|
|
## Controlled resolution — never reset journals
|
|
|
|
1. Stop/fence all custody writers and preserve matching database/node evidence and backups. Diagnose privately; a public-facing error intentionally does not disclose the affected customer.
|
|
2. Have the responsible operator/reviewer establish the correct state from immutable receipts/journals, trusted backup evidence and any external effects. This increment supplies **no automatic repair, compensation, destructive reset or payout retry**. Repair/restore authorization is separate from acknowledging an incident.
|
|
3. Using the schema-owner credential, run the existing database CLI action `resolve-integrity EVIDENCE_UUID`. The UUID refers to the externally maintained reviewed incident case; the tool does not certify the truth of that case or supply a second human approval.
|
|
4. [Resolution code](../server/market/custody-incident.ts) takes the financial writer lock, verifies schema-owner membership, tentatively removes the unresolved marker and runs the full accounting/business audit in the same transaction. Failure rolls everything back, preserving the hold. Success appends a resolution record with evidence UUID, timestamp and incident digest; it changes no journal/account/business rows.
|
|
5. Restart previously latched instances only after controlled resolution, then verify successful startup/audit and operations state. A detecting instance stays locally held even after a successful later audit. The restricted runtime role cannot acknowledge incidents or delete the marker.
|
|
|
|
This process is intentionally not a “clear error” button. Manual SQL balance edits, deleting a marker outside the reviewed procedure, restoring stale isolated financial state or inventing journal resets can create duplicate payouts or loss of liabilities. An external database owner retains administrative power; independent evidence retention remains necessary.
|
|
|
|
## Evidence and limitations
|
|
|
|
Tests cover business-record corruptions that leave all journal totals balanced; bad allocations/fee metadata; missing/mismatched outbox; orphan reserves/tasks; targeted mutation rollback; durable cross-instance/startup holds; private reads/original replay during a hold; restricted-role resolution denial; failed-resolution rollback; successful reviewed acknowledgement plus restart; traversal beyond 128 records; and import of historical fixture completions/cancellations. No actual fund transfer, incident on deployed user data or consensus change was performed.
|
|
|
|
Final validation: TypeScript/Vite Docker build passed; **168 offline regression tests and 16 real-PostgreSQL integration tests passed**, zero failures/skips. The first corruption-test run exposed an unguarded chained amount refinement on malformed stored decimals; the parser was corrected to emit a redacted integrity incident and all suites were rerun. Deployed sandbox services were not recreated or subjected to deliberate corruption; new code takes effect only after the selected runtime image is rebuilt/recreated.
|
|
|
|
SQLite remains a disposable compatibility model with its original journal audit; this richer runtime checking is implemented in the PostgreSQL backend, including imported SQLite histories. Source-freeze/import procedures are unchanged. Production live-deposit attribution, treasury asset reconciliation, normalized financial states, compensation, observer/signing services, independent review and measured load/SLOs remain open. A successful internal audit is **not** proof of external XMR reserves, a guarantee of solvency, or launch permission.
|