Disposable canonical-event inbox rehearsal

This commit is contained in:
2026-10-20 18:50:02 -04:00
parent ca8ff48628
commit e1fbf5e5af
7 changed files with 342 additions and 6 deletions
+10 -3
View File
@@ -31,11 +31,17 @@ The existing sandbox `mock-sale-settlement` journal and `fixture-complete`/`fixt
A submitted history that conflicts with, removes or makes immature an already posted outcome triggers `sale-rehearsal-outcome-reorg`. Business changes roll back, and a redacted durable integrity incident commits while retaining the shared writer lock. All custody instances then reject new mutations; private reads and original signed receipts remain available. The original seller credit/refund stays recorded: no automatic compensation, reversal or second payout. Re-submitting the old mature history cannot clear the incident. Existing schema-owner incident review procedures are not a substitute for resolving chain risk; production resolution requires retained canonical evidence and approved loss policy.
This rehearsal is **not a durable connect/disconnect observer**. It retains first/terminal evidence and the redacted incident reason, not every submitted fork transcript. It cannot detect an intermediate disconnect that is never submitted, even if an outcome later reappears. The caller must retain conflicting evidence for investigation. Production must integrate a replay-safe canonical event inbox/cursor, history anchors, disconnect-before-reconnect handling and persistent per-order review. No monotonic canonical-height guarantee is claimed for this caller-driven seam.
## Local canonical-event inbox foundation
The same explicitly disposable PostgreSQL instance now has `observeCanonicalEvents()` and `observeLedgerCanonicalEvents(ledger)`. The ledger adapter requires an actual in-process `Ledger` object, checks its network/chain identity, and fetches one page at a time from its locally validated [canonical event API](../server/testnet/canonical-index.ts)—no caller/HTTP/peer page can enter through that adapter. The inbox is append-only and bounded. It checks stream identity, page limits, predecessor anchors, event hashes, sequence continuity, connect parent/tip and tip-first disconnect rules against a reconstructed canonical hash stack. It accepts exact retries and overlapping pages from concurrent consumers only after matching their persisted overlap; stale retries report the durable cursor/head, not the stale page head. The startup/periodic financial audit replays and verifies the stored inbox; a changed stream, gap or malformed/tampered event creates the normal durable integrity hold. Inbox records use namespaced `custody_meta` keys and the existing runtime role; there is no schema migration or mutable cursor.
The adapter is not automatically scheduled: there is no HTTP route or background worker. PostgreSQL tests include synthetic event pages for malformed/retry cases and one integration using blocks accepted by a local `Ledger` configured with a **test-only fake PoW engine**, including an equal-work side branch and a reorg. This tests the local adapter/API boundary, not real RandomX validation, external provenance or production multi-node behavior. Hash-anchor consistency is not proof of honest observation; the stream ID and unkeyed hashes are stored beside the events, so a schema owner able to coherently rewrite the complete history can also recompute the hashes. The included corruption test detects a partial edit, not a fully rewritten valid history. Production must anchor custody checkpoints outside the database's mutable trust domain. The disposable inbox is capped at 4,096 total events, stores the append-only event history and fully rescans it during audits. That is intentionally a small-fixture design, not production storage/cursor architecture or a scale claim.
Most importantly, the inbox is not connected to candidate transaction IDs, `replayReservedSale()` or settlement decisions. Existing v6 blocks cannot contain candidate sale transactions, and local free transfers are not accepted as sale completion evidence. It therefore **does not create a sale-specific disconnect hold** or detect an unsubmitted intermediate fork that later disappears before a page is observed. The synthetic sale transcript still retains only first/terminal evidence and the redacted incident reason; the caller must retain conflicting transcript evidence. Production must integrate a durable replay-safe canonical consumer with per-order candidate transaction linkage, then persist a review hold for a previously posted outcome. No production sale-observation guarantee is claimed for this increment.
## Validation and remaining gates
TypeScript/Vite Docker build passed; **172 offline regressions and 22 PostgreSQL integration tests passed, zero failures/skips**. [Candidate tests](../tests/sale-candidate.test.ts) cover signature compatibility/recipient tampering and candidate transition rules. [PostgreSQL tests](../tests/postgres/custody.test.ts) add:
TypeScript/Vite Docker build passed; **172 offline regressions and 27 PostgreSQL integration tests passed, zero failures/skips**. [Candidate tests](../tests/sale-candidate.test.ts) cover signature compatibility/recipient tampering and candidate transition rules. [PostgreSQL tests](../tests/postgres/custody.test.ts) add:
- explicit instance/database consent and exact signed reservation matching;
- seed-binding changes, malformed/cyclic/oversized collections, and denial of ordinary cancellation after binding;
@@ -44,8 +50,9 @@ TypeScript/Vite Docker build passed; **172 offline regressions and 22 PostgreSQL
- identical-rounded-allocation but different fee-rate rejection;
- one-atomic settlement and signed custom-recipient revenue across current-policy changes;
- conflicting outcome → durable cross-instance/restart hold without a second credit;
- anchored canonical-event connect/disconnect paging, overlapping/stale consumer retries, reconnect, partial history mutation audit, forged disconnect-ancestor rejection and tamper-triggered hold; actual validated local-ledger blocks, excluded equal-work branch and a canonical reorg through the adapter (no candidate sale link);
- injected terminal-evidence failure → complete rollback and safe retry; corrupted evidence → audit hold.
A first bounded-checkpoint implementation attempted metadata UPDATE and was rejected by the real runtime grants. It was corrected to retain insert-only first/terminal evidence; no privilege was widened. Test evidence is for this implementation boundary, not production approval.
**M3 remains open.** Next work is independently reviewed protocol/policy and explicit new-network activation design, then actual PoW/peer/index/undo integration and durable canonical observation tied to financially fenced host-signing intent. Monero deposit confirmation/unlock observation, treasury/signer separation, real withdrawal controls, coordinated recovery, capacity and legal/operational release gates remain separate. The active v6 chain still rejects every candidate sale transaction. Persistent local custody deployments were not switched or restarted by this rehearsal.
**M3 remains open.** Next work is independently reviewed protocol/policy and explicit new-network activation design, then actual PoW/peer/index/undo integration and durable canonical observation tied to financially fenced host-signing intent. Monero deposit confirmation/unlock observation, treasury/signer separation, real withdrawal controls, coordinated recovery, capacity and legal/operational release gates remain separate. The active v6 chain still rejects every candidate sale transaction. Persistent local custody deployments were not switched or restarted by this rehearsal.