@bsv/overlay 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +57 -27
- package/README.md +102 -269
- package/dist/cjs/mod.d.ts +15 -0
- package/dist/cjs/mod.d.ts.map +1 -0
- package/dist/cjs/mod.js +4 -1
- package/dist/cjs/mod.js.map +1 -1
- package/dist/cjs/package.json +41 -13
- package/dist/cjs/src/Advertisement.d.ts +9 -0
- package/dist/cjs/src/Advertisement.d.ts.map +1 -0
- package/dist/cjs/src/Advertiser.d.ts +36 -0
- package/dist/cjs/src/Advertiser.d.ts.map +1 -0
- package/dist/cjs/src/BASM.d.ts +110 -0
- package/dist/cjs/src/BASM.d.ts.map +1 -0
- package/dist/cjs/src/BASM.js.map +1 -1
- package/dist/cjs/src/BASMRemote.d.ts +14 -0
- package/dist/cjs/src/BASMRemote.d.ts.map +1 -0
- package/dist/cjs/src/BASMRemote.js.map +1 -1
- package/dist/cjs/src/Engine.d.ts +379 -0
- package/dist/cjs/src/Engine.d.ts.map +1 -0
- package/dist/cjs/src/Engine.js +425 -418
- package/dist/cjs/src/Engine.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +24 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPRemote.js +7 -10
- package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +103 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -0
- package/dist/cjs/src/GASP/OverlayGASPStorage.js +32 -30
- package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/cjs/src/LookupFormula.d.ts +29 -0
- package/dist/cjs/src/LookupFormula.d.ts.map +1 -0
- package/dist/cjs/src/LookupService.d.ts +85 -0
- package/dist/cjs/src/LookupService.d.ts.map +1 -0
- package/dist/cjs/src/Output.d.ts +32 -0
- package/dist/cjs/src/Output.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.d.ts +14 -0
- package/dist/cjs/src/SafeLog.d.ts.map +1 -0
- package/dist/cjs/src/SafeLog.js +44 -0
- package/dist/cjs/src/SafeLog.js.map +1 -0
- package/{src/SyncConfiguration.ts → dist/cjs/src/SyncConfiguration.d.ts} +2 -1
- package/dist/cjs/src/SyncConfiguration.d.ts.map +1 -0
- package/dist/cjs/src/TopicManager.d.ts +35 -0
- package/dist/cjs/src/TopicManager.d.ts.map +1 -0
- package/dist/cjs/src/storage/Storage.d.ts +237 -0
- package/dist/cjs/src/storage/Storage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts +69 -0
- package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/KnexStorage.js +7 -10
- package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/cjs/src/storage/knex/all-migrations.d.ts +11 -0
- package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-05-18-001-initial.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-10-001-block-height.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-17-001-transactions.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2024-07-18-001-indexes.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-05-28-001-enlarge.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts +9 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.js.map +1 -1
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts +4 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.d.ts.map +1 -0
- package/dist/cjs/src/storage/knex/migrations/2026-05-29-001-brc136-basm.js.map +1 -1
- package/dist/esm/mod.js +2 -1
- package/dist/esm/mod.js.map +1 -1
- package/dist/esm/src/BASMRemote.js.map +1 -1
- package/dist/esm/src/Engine.js +404 -396
- package/dist/esm/src/Engine.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPRemote.js +2 -2
- package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
- package/dist/esm/src/GASP/OverlayGASPStorage.js +21 -18
- package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
- package/dist/esm/src/SafeLog.js +40 -0
- package/dist/esm/src/SafeLog.js.map +1 -0
- package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
- package/dist/types/mod.d.ts +5 -4
- package/dist/types/mod.d.ts.map +1 -1
- package/dist/types/src/BASMRemote.d.ts.map +1 -1
- package/dist/types/src/Engine.d.ts +18 -1
- package/dist/types/src/Engine.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts +2 -2
- package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts +3 -1
- package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
- package/dist/types/src/SafeLog.d.ts +14 -0
- package/dist/types/src/SafeLog.d.ts.map +1 -0
- package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
- package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
- package/package.json +85 -28
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +0 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +0 -1
- package/dist/types/tsconfig.types.tsbuildinfo +0 -1
- package/docs/API.md +0 -944
- package/docs/BRC-136-BASM.md +0 -327
- package/docs/README.md +0 -16
- package/docs/Synchronization.md +0 -212
- package/docs/concepts/00-overview.md +0 -85
- package/docs/concepts/01-best-practices.md +0 -202
- package/docs/concepts/02-query-performance.md +0 -345
- package/docs/concepts/03-database-monitoring.md +0 -211
- package/docs/concepts/04-pagination-example.md +0 -186
- package/docs/concepts/05-recommendations-summary.md +0 -158
- package/docs/concepts/README.md +0 -20
- package/docs/examples/README.md +0 -15
- package/docs/examples/gs-wip.md +0 -111
- package/docs/internal/README.md +0 -11
- package/mod.ts +0 -40
- package/src/Advertisement.ts +0 -8
- package/src/Advertiser.ts +0 -41
- package/src/BASM.ts +0 -208
- package/src/BASMRemote.ts +0 -54
- package/src/Engine.ts +0 -2194
- package/src/GASP/OverlayGASPRemote.ts +0 -108
- package/src/GASP/OverlayGASPStorage.ts +0 -384
- package/src/LookupFormula.ts +0 -31
- package/src/LookupService.ts +0 -136
- package/src/Output.ts +0 -31
- package/src/TopicManager.ts +0 -40
- package/src/__tests/BASM.test.ts +0 -44
- package/src/__tests/BASMChain.test.ts +0 -295
- package/src/__tests/Engine.test.ts +0 -1381
- package/src/__tests/OverlayGASPRemote.test.ts +0 -135
- package/src/__tests/OverlayGASPStorage.test.ts +0 -205
- package/src/storage/Storage.ts +0 -260
- package/src/storage/knex/KnexStorage.ts +0 -651
- package/src/storage/knex/all-migrations.ts +0 -32
- package/src/storage/knex/migrations/2024-05-18-001-initial.ts +0 -30
- package/src/storage/knex/migrations/2024-07-10-001-block-height.ts +0 -15
- package/src/storage/knex/migrations/2024-07-17-001-transactions.ts +0 -64
- package/src/storage/knex/migrations/2024-07-18-001-indexes.ts +0 -30
- package/src/storage/knex/migrations/2025-05-28-001-enlarge.ts +0 -42
- package/src/storage/knex/migrations/2025-06-25-001-gasp-pagination-support.ts +0 -46
- package/src/storage/knex/migrations/2025-07-22-001-fix-score-column-type.ts +0 -24
- package/src/storage/knex/migrations/2025-11-11-001-utxo-lookup-index.ts +0 -18
- package/src/storage/knex/migrations/2026-05-29-001-brc136-basm.ts +0 -123
package/docs/BRC-136-BASM.md
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
# BRC-136 BASM Synchronization & Admin/Janitor Removal
|
|
2
|
-
|
|
3
|
-
This document explains how the BRC-136 Block-Anchored synchronization layer
|
|
4
|
-
([spec](https://bsv.brc.dev/overlays/0136)) is implemented in this engine, and —
|
|
5
|
-
critically — **how it interacts with admin and janitor removal of outputs from a
|
|
6
|
-
topic**. The two systems are deliberately decoupled. Read the final section
|
|
7
|
-
("How BASM and removal interact") before operating a node that runs the janitor
|
|
8
|
-
or removes tokens via the admin API.
|
|
9
|
-
|
|
10
|
-
## What BRC-136 gives you
|
|
11
|
-
|
|
12
|
-
GASP ([Synchronization.md](./Synchronization.md)) replicates transaction *data*
|
|
13
|
-
between peers. BRC-136 sits alongside it and answers a different question:
|
|
14
|
-
|
|
15
|
-
> Do two overlays **agree about which transactions were admitted to a topic** at
|
|
16
|
-
> every block height, from the topic's genesis to the chain tip?
|
|
17
|
-
|
|
18
|
-
It does this with a per-topic **Topic Anchor Chain (TAC)** — a cumulative hash
|
|
19
|
-
that commits to every block's admitted-transaction set. If two nodes report the
|
|
20
|
-
same `TAC(topic, H)`, they provably agree on every admitted transaction for that
|
|
21
|
-
topic from genesis through height `H`. A single round trip detects agreement; a
|
|
22
|
-
binary search over heights localizes any divergence in `O(log H)` rounds.
|
|
23
|
-
|
|
24
|
-
### Core data structures
|
|
25
|
-
|
|
26
|
-
| Concept | Type | Source of truth |
|
|
27
|
-
|---|---|---|
|
|
28
|
-
| Admitted set at a block | `AdmittedTxRef[]` | `applied_transactions` table |
|
|
29
|
-
| Block anchor | `TopicBlockAnchor` | `topic_block_anchors` table |
|
|
30
|
-
| Cumulative chain hash | `tac` field on the anchor | derived |
|
|
31
|
-
|
|
32
|
-
- **`basmRoot`** — Merkle root over the admitted txids at one block height, in
|
|
33
|
-
canonical block order. Empty set → 32 zero bytes; single txid → the txid
|
|
34
|
-
itself (unhashed); multiple → `SHA256d` binary tree with Bitcoin odd-leaf
|
|
35
|
-
duplication. See `computeBasmRoot` in `src/BASM.ts`.
|
|
36
|
-
- **`tac`** — `SHA256d(prevTac ‖ blockHash ‖ basmRoot)`, all inputs in internal
|
|
37
|
-
byte order. See `computeTac` in `src/BASM.ts`.
|
|
38
|
-
|
|
39
|
-
### Gap-free chains
|
|
40
|
-
|
|
41
|
-
The TAC must advance on **every** block — including blocks with no admitted
|
|
42
|
-
transactions — or peers could not distinguish "no transactions here" from "I
|
|
43
|
-
haven't synced this height." The engine keeps chains contiguous:
|
|
44
|
-
|
|
45
|
-
- `advanceTopicAnchorChains(toHeight)` extends each topic forward with empty
|
|
46
|
-
anchors (zero root, zero count) up to `toHeight`.
|
|
47
|
-
- `rebuildTopicAnchorChain(...)` / `recomputeTopicBlockAnchor(...)` rebuild a
|
|
48
|
-
contiguous slice after an admission (including out-of-order proofs), filling
|
|
49
|
-
any missing heights with empty anchors rather than leaving a hole.
|
|
50
|
-
|
|
51
|
-
A chain only *starts* at the topic's first admitted height (its genesis).
|
|
52
|
-
Covered by `src/__tests/BASMChain.test.ts`.
|
|
53
|
-
|
|
54
|
-
## The admitted set is a historical fact, not current UTXO state
|
|
55
|
-
|
|
56
|
-
This is the key design decision, and the source of the assurance this layer
|
|
57
|
-
provides.
|
|
58
|
-
|
|
59
|
-
The admitted set for `(topic, height)` is read by
|
|
60
|
-
`Storage.findAdmittedTransactionsForBlock`, which queries:
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
applied_transactions WHERE topic = ? AND blockHeight = ? AND proven = true AND blockIndex IS NOT NULL
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
It is **append-only for proven transactions**. A row lands here when a
|
|
67
|
-
transaction is admitted to the topic and has a merkle proof
|
|
68
|
-
(`buildAppliedTransactionRecord` sets `proven: true` only when proof metadata is
|
|
69
|
-
present). From that point the row — and therefore the `basmRoot` and `tac` that
|
|
70
|
-
commit to it — is an immutable record of "this overlay received and admitted
|
|
71
|
-
this txid at this block height."
|
|
72
|
-
|
|
73
|
-
It is **not** derived from current unspent outputs. Spending an output, pruning
|
|
74
|
-
its history, or removing it from lookup does not change the admitted set.
|
|
75
|
-
|
|
76
|
-
## The lookup set is separate, mutable, and policy-driven
|
|
77
|
-
|
|
78
|
-
What an overlay *serves* in lookup responses is a different set, in different
|
|
79
|
-
storage:
|
|
80
|
-
|
|
81
|
-
- The engine `outputs` table, filtered to `spent: false`.
|
|
82
|
-
- The lookup service's own index (e.g. the Mongo `shipRecords` / `slapRecords`
|
|
83
|
-
collections for the discovery services).
|
|
84
|
-
|
|
85
|
-
This set is mutable by design. Outputs get marked spent, pruned from history,
|
|
86
|
-
removed by an admin, or deleted by the janitor.
|
|
87
|
-
|
|
88
|
-
## Removal paths — exactly what each one touches
|
|
89
|
-
|
|
90
|
-
| Path | Code | Touches lookup set | Touches admitted set (`applied_transactions`) |
|
|
91
|
-
|---|---|---|---|
|
|
92
|
-
| Spend / history prune | `Engine.deleteUTXODeep` → `deleteOutput` | yes | **no** |
|
|
93
|
-
| Admin remove-token | `/admin/remove-token` → `evictFromServices` → `LookupService.outputEvicted` | yes (lookup index) | **no** |
|
|
94
|
-
| Janitor (dead SHIP/SLAP host) | `JanitorService.handleUnhealthyOutput` → `deleteOne` + ban | yes (lookup index) | **no** |
|
|
95
|
-
| Ban enforcement | `BanAwareLookupWrapper.outputAdmittedByTopic` | blocks re-entry into lookup index on GASP re-sync | **no** |
|
|
96
|
-
| Unproven eviction | `Engine.evictUnprovenTransactions` → `deleteAppliedTransaction` | yes | yes — **but only `proven: false` rows** |
|
|
97
|
-
|
|
98
|
-
Notes:
|
|
99
|
-
|
|
100
|
-
- **`deleteUTXODeep` never deletes the applied-transaction record.** It calls
|
|
101
|
-
`deleteOutput` only. The admission proof survives a normal spend or prune.
|
|
102
|
-
- **Admin and janitor removal operate on the lookup layer only.** Admin
|
|
103
|
-
`evictFromServices` calls `LookupService.outputEvicted`; the janitor deletes
|
|
104
|
-
from its own Mongo collection and writes to the ban list. Neither calls
|
|
105
|
-
`deleteAppliedTransaction`, `deleteOutput` on proven engine state, or anything
|
|
106
|
-
that mutates `topic_block_anchors`.
|
|
107
|
-
- **The ban list is a lookup-layer filter.** `BanAwareLookupWrapper` blocks
|
|
108
|
-
banned outpoints/domains at `outputAdmittedByTopic`, preventing a removed token
|
|
109
|
-
from re-entering the *lookup index* when GASP re-syncs it from a peer. It does
|
|
110
|
-
not — and must not — block the BRC-136 admitted record.
|
|
111
|
-
- **`evictUnprovenTransactions` is the only path that deletes an
|
|
112
|
-
applied-transaction row**, and it filters to `proven: false`. Such rows never
|
|
113
|
-
appear in any anchor (the anchor query requires `proven: true`), so evicting
|
|
114
|
-
them cannot change any `basmRoot` or `tac`.
|
|
115
|
-
|
|
116
|
-
## How BASM and removal interact
|
|
117
|
-
|
|
118
|
-
This is the question operators most need answered.
|
|
119
|
-
|
|
120
|
-
### You can always prove you received and processed a token
|
|
121
|
-
|
|
122
|
-
Even after the janitor deletes a dead host's advertisement, or an admin removes a
|
|
123
|
-
malicious output via `/admin/remove-token` (with or without a ban), the overlay
|
|
124
|
-
**retains full proof that it received and admitted that token**:
|
|
125
|
-
|
|
126
|
-
- the `applied_transactions` row (`proven: true`, with `blockHeight`,
|
|
127
|
-
`blockIndex`, `blockHash`, `merkleRoot`),
|
|
128
|
-
- the `topic_block_anchors` row whose `basmRoot` commits to that txid, and
|
|
129
|
-
- the ability to serve a compound merkle path for it via the BRC-136 endpoints.
|
|
130
|
-
|
|
131
|
-
Removal is a **lookup-serving policy decision**. It changes what the node returns
|
|
132
|
-
to lookup queries. It does not, and cannot, rewrite the historical admitted set
|
|
133
|
-
or the TAC.
|
|
134
|
-
|
|
135
|
-
### TAC agreement means agreement on *admission*, not on *lookup results*
|
|
136
|
-
|
|
137
|
-
This is the most important operational caveat:
|
|
138
|
-
|
|
139
|
-
> **A matching TAC proves two overlays agree on which transactions were admitted.
|
|
140
|
-
> It does NOT prove they will return identical lookup results.**
|
|
141
|
-
|
|
142
|
-
Bans and removals are **per-node policy and are not synchronized between peers**.
|
|
143
|
-
Two honest overlays running the same topic manager can therefore:
|
|
144
|
-
|
|
145
|
-
- report the **same** `TAC(topic, H)` (identical admitted sets), while
|
|
146
|
-
- returning **different** lookup results — because one node banned a malicious
|
|
147
|
-
output that the other still serves.
|
|
148
|
-
|
|
149
|
-
This is correct and intended. BRC-136 anchors *receipt and admission*, which is
|
|
150
|
-
an objective, deterministic function of chain state and the topic manager. It
|
|
151
|
-
intentionally does not anchor each node's local moderation policy.
|
|
152
|
-
|
|
153
|
-
### Consequence: divergence diagnosis
|
|
154
|
-
|
|
155
|
-
- **TACs match** → the two nodes admitted exactly the same transactions. Any
|
|
156
|
-
difference in lookup output is explained by local removal/ban policy, not by a
|
|
157
|
-
sync problem.
|
|
158
|
-
- **TACs diverge** → the nodes genuinely disagree about admission at some height
|
|
159
|
-
(e.g. different topic-manager versions, a missed transaction, or a reorg
|
|
160
|
-
handled differently). This is a real sync issue; the binary-search localizes
|
|
161
|
-
the height.
|
|
162
|
-
|
|
163
|
-
Removing a token from lookup will **not** cause TAC divergence. If you ever see
|
|
164
|
-
TAC divergence after only running the janitor/admin removal, that is a bug —
|
|
165
|
-
something is incorrectly deleting proven `applied_transactions` rows or mutating
|
|
166
|
-
anchors.
|
|
167
|
-
|
|
168
|
-
### Caveat for the GASP validation guidance
|
|
169
|
-
|
|
170
|
-
[Synchronization.md](./Synchronization.md) suggests validating sync by comparing
|
|
171
|
-
per-topic SQL row counts across nodes. Once the janitor or admin removal is
|
|
172
|
-
active, **lookup-set counts can legitimately differ between nodes even when their
|
|
173
|
-
TACs match**. To validate BRC-136 agreement, compare `TAC(topic, tip)` between
|
|
174
|
-
peers — not lookup row counts.
|
|
175
|
-
|
|
176
|
-
## Reorg handling
|
|
177
|
-
|
|
178
|
-
A blockchain reorganization can orphan a block whose transactions were already
|
|
179
|
-
admitted and anchored. Since anchors and the admitted set are keyed by
|
|
180
|
-
`(topic, blockHeight, blockHash)`, an orphaned block would otherwise leave stale
|
|
181
|
-
`applied_transactions` rows and an anchor whose `basmRoot`/`tac` commit to a
|
|
182
|
-
block hash that is no longer canonical — permanently diverging the TAC from peers
|
|
183
|
-
that observed the reorg. The engine reconciles this automatically.
|
|
184
|
-
|
|
185
|
-
### Chaintracks is the reorg authority
|
|
186
|
-
|
|
187
|
-
Reorg detection is **not** reinvented here. The production chain tracker should
|
|
188
|
-
be a go-chaintracks compatible service. Arcade exposes go-chaintracks under
|
|
189
|
-
`/chaintracks/v2`, and a standalone go-chaintracks deployment may expose the same
|
|
190
|
-
API at `/v2`:
|
|
191
|
-
|
|
192
|
-
- **Arcade-mounted Chaintracks:** `GET /chaintracks/v2/reorg/stream`
|
|
193
|
-
- **Standalone go-chaintracks:** `GET /v2/reorg/stream`
|
|
194
|
-
|
|
195
|
-
The stream emits `data: <JSON>\n\n` frames (no `event:`/`id:` lines;
|
|
196
|
-
`: keepalive` comments between events). Each frame is a `ReorgEvent`:
|
|
197
|
-
|
|
198
|
-
```jsonc
|
|
199
|
-
{
|
|
200
|
-
"orphanedHashes": ["<blockHash>", ...], // blocks removed from the active chain
|
|
201
|
-
"commonAncestor": { "height": 100, "hash": "..." },
|
|
202
|
-
"newTip": { "height": 103, "hash": "..." },
|
|
203
|
-
"depth": 3
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
- The same go-chaintracks-compatible service answers the merkle-root checks
|
|
208
|
-
(`isValidRootForHeight`) the engine already trusts for SPV verification.
|
|
209
|
-
|
|
210
|
-
`packages/overlays/overlay-express/src/ReorgStream.ts` consumes this stream and
|
|
211
|
-
maps each event to the engine: `orphanedHashes` → blocks to reconcile,
|
|
212
|
-
`commonAncestor.height + 1` → rebuild floor, `newTip.height` → rebuild ceiling.
|
|
213
|
-
Block hashes are lower-cased to match stored display-hex hashes (go-sdk
|
|
214
|
-
`chainhash.Hash` marshals as reversed/display hex).
|
|
215
|
-
|
|
216
|
-
> **No replay on this stream.** It carries no event ids, so a reorg that fires
|
|
217
|
-
> while the SSE client is disconnected is lost. Every (re)connect therefore runs a
|
|
218
|
-
> catch-up revalidation sweep (below), and the periodic block poll runs the same
|
|
219
|
-
> sweep as a standing fallback for chain trackers that expose no reorg stream.
|
|
220
|
-
|
|
221
|
-
### What the engine does on a reorg — `Engine.handleReorg`
|
|
222
|
-
|
|
223
|
-
1. **Demote orphaned admissions.** For each orphaned block hash, every **proven**
|
|
224
|
-
`applied_transactions` row anchored to it is demoted to unproven
|
|
225
|
-
(`demoteAppliedTransactionToUnproven`): block height/hash/index/merkleRoot are
|
|
226
|
-
cleared and `proven` is set to `false`, **but `firstSeenHeight` is kept**. The
|
|
227
|
-
transaction immediately leaves the admitted set.
|
|
228
|
-
2. **Rebuild affected anchor chains.** Every topic whose chain intersects the
|
|
229
|
-
reorged height range is rebuilt over the **canonical** block hashes (the rebuild
|
|
230
|
-
forces header re-resolution rather than reusing the stale stored hash). Topics
|
|
231
|
-
with no demoted transaction are rebuilt too, because the canonical block hash
|
|
232
|
-
for those heights changed and the `tac` must re-chain over it.
|
|
233
|
-
|
|
234
|
-
`handleReorg` is **idempotent**: a clean window demotes nothing and reproduces an
|
|
235
|
-
identical TAC, so it is safe to run on every reorg event, SSE reconnect, and poll.
|
|
236
|
-
|
|
237
|
-
### Fallback / catch-up — `Engine.revalidateRecentAnchors(depth = 3)`
|
|
238
|
-
|
|
239
|
-
Scans proven `applied_transactions` in `[tip - depth + 1, tip]`. Any row whose
|
|
240
|
-
proof root no longer validates (`isValidRootForHeight`) or whose block hash
|
|
241
|
-
diverges from the canonical header is treated as orphaned and fed into
|
|
242
|
-
`handleReorg`. This is the reorg path for chain trackers without a reorg stream,
|
|
243
|
-
and the catch-up step on every SSE reconnect. Default depth is 3 blocks
|
|
244
|
-
(`reorgScanDepth`, configurable via `configureReorgStream`).
|
|
245
|
-
|
|
246
|
-
### Interaction with admin/janitor removal
|
|
247
|
-
|
|
248
|
-
Reorg recovery and lookup-layer removal are independent and do not interfere:
|
|
249
|
-
|
|
250
|
-
- Removal/bans touch only the lookup layer; they never demote `applied_transactions`.
|
|
251
|
-
- Reorg demotion touches only the admitted set (proven → unproven) via the chain
|
|
252
|
-
tracker; it never consults the ban list or lookup index.
|
|
253
|
-
|
|
254
|
-
A demoted transaction follows the engine's unproven lifecycle. The preferred
|
|
255
|
-
maintenance path is refresh-before-evict:
|
|
256
|
-
|
|
257
|
-
1. If the transaction is re-mined and a provider calls `/arc-ingest` with a
|
|
258
|
-
proof, `handleNewMerkleProof` re-proves it at its new height and the anchor
|
|
259
|
-
includes it again.
|
|
260
|
-
2. If no callback arrives, `refreshUnprovenTransactionProofs` asks configured
|
|
261
|
-
proof providers such as Arcade for a fresh proof.
|
|
262
|
-
3. `maintainUnprovenTransactions` refreshes proofs first, then calls
|
|
263
|
-
`evictUnprovenTransactions` for rows that are still unproven past the
|
|
264
|
-
configured threshold.
|
|
265
|
-
|
|
266
|
-
The "we received it" record survives until a proof, a terminal provider
|
|
267
|
-
invalidation, or age-based eviction resolves it.
|
|
268
|
-
|
|
269
|
-
### Provider invalidation and double spends
|
|
270
|
-
|
|
271
|
-
Provider callbacks can also report terminal rejection. When `/arc-ingest`
|
|
272
|
-
classifies a callback as double spend or another terminal invalid outcome, the
|
|
273
|
-
Express layer evicts the applied transaction immediately through
|
|
274
|
-
`Engine.evictAppliedTransaction`. This removes the transaction from the admitted
|
|
275
|
-
set and notifies lookup services through their `outputEvicted` path. It is more
|
|
276
|
-
important to stop serving rejected data than to wait for the normal unproven
|
|
277
|
-
eviction threshold.
|
|
278
|
-
|
|
279
|
-
### Configuration
|
|
280
|
-
|
|
281
|
-
```ts
|
|
282
|
-
server.configureChaintracks('https://arcade.example', {
|
|
283
|
-
apiPrefix: '/chaintracks/v2',
|
|
284
|
-
reorgStream: true,
|
|
285
|
-
scanDepth: 3
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
server.configureEnableBASMSync(true)
|
|
289
|
-
server.configureUnprovenMaintenance({
|
|
290
|
-
thresholdBlocks: 144,
|
|
291
|
-
intervalMs: 60 * 60 * 1000
|
|
292
|
-
})
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
- `apiPrefix` — `/chaintracks/v2` for Arcade-mounted Chaintracks; `/v2` for many
|
|
296
|
-
standalone go-chaintracks deployments.
|
|
297
|
-
- `reorgStream` — when enabled, the SSE adapter reconciles reorgs in real time.
|
|
298
|
-
- `scanDepth` — sweep depth from tip (default `3`).
|
|
299
|
-
- `thresholdBlocks` — how old an unproven row must be before maintenance tries
|
|
300
|
-
proof refresh and eviction.
|
|
301
|
-
- The block poll (`basmBlockPollIntervalMs`) runs the sweep as a fallback even
|
|
302
|
-
when no stream is configured.
|
|
303
|
-
|
|
304
|
-
## Verification checklist for operators
|
|
305
|
-
|
|
306
|
-
1. After removing a token, confirm its `applied_transactions` row still exists
|
|
307
|
-
(`proven: true`) and the `topic_block_anchors` row for its height is unchanged.
|
|
308
|
-
2. Confirm `TAC(topic, tip)` is unchanged by the removal.
|
|
309
|
-
3. Confirm the token no longer appears in lookup responses (and, if banned, does
|
|
310
|
-
not reappear after a GASP re-sync).
|
|
311
|
-
|
|
312
|
-
If all three hold, removal is behaving correctly: the node no longer serves the
|
|
313
|
-
token, yet can still prove it was received and admitted.
|
|
314
|
-
|
|
315
|
-
### Reorg verification
|
|
316
|
-
|
|
317
|
-
4. After a reorg orphans an admitted transaction's block, confirm its
|
|
318
|
-
`applied_transactions` row is demoted (`proven: false`, block fields cleared,
|
|
319
|
-
`firstSeenHeight` retained) and the affected `topic_block_anchors` rows now
|
|
320
|
-
carry the canonical block hashes with a recomputed `tac`.
|
|
321
|
-
5. Confirm `TAC(topic, tip)` reconverges with peers that observed the same reorg.
|
|
322
|
-
6. If the orphaned transaction is re-mined, confirm `/arc-ingest` or
|
|
323
|
-
`refreshUnprovenTransactionProofs` re-proves it and the anchor re-includes it.
|
|
324
|
-
Otherwise confirm `maintainUnprovenTransactions` eventually evicts it as
|
|
325
|
-
unproven.
|
|
326
|
-
7. If a provider reports a double spend, confirm the applied transaction is
|
|
327
|
-
evicted immediately and lookup services no longer return it.
|
package/docs/README.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# Overlay Services Engine Documentation
|
|
2
|
-
|
|
3
|
-
The documentation is split into various pages, each covering a set of related functionality. The pages are as follows:
|
|
4
|
-
|
|
5
|
-
## Main Documentation
|
|
6
|
-
|
|
7
|
-
- [API](./API.md) — How to interact with the Overlay Services Engine
|
|
8
|
-
- [Synchronization](./Synchronization.md) — GASP peer synchronization
|
|
9
|
-
- [BRC-136 BASM](./BRC-136-BASM.md) — Block-anchored sync, and how it interacts with admin/janitor removal
|
|
10
|
-
- [Concepts](./concepts/README.md) — High-level concepts
|
|
11
|
-
- [Examples](./examples/README.md) — Guides, tutorials and examples of using the Engine
|
|
12
|
-
- [Internal](./internal/README.md) — References for working with internal system components
|
|
13
|
-
|
|
14
|
-
---
|
|
15
|
-
|
|
16
|
-
[🏠 Home](./README.md) | [📚 API](./API.md) | [💡 Concepts](./concepts/README.md) | [📖 Examples](./examples/README.md) | [⚙️ Internal](./internal/README.md)
|
package/docs/Synchronization.md
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
## About
|
|
2
|
-
|
|
3
|
-
**GASP (Graph Aware Sync Protocol)** is the synchronization protocol overlay nodes use to replicate overlay-relevant transaction data between peers in a way that is **verifiable**, **complete**, and **bandwidth-efficient**. Instead of just sending raw TXIDs or copying a flat UTXO list, GASP reconciles **transaction graphs** by exchanging transactions/outputs with proof material and then **recursively requesting any missing input transactions** needed to validate what was received.
|
|
4
|
-
|
|
5
|
-
At a high level, GASP is built around:
|
|
6
|
-
|
|
7
|
-
- **Legitimacy:** Nodes only finalize data they can validate by anchoring it back to the blockchain (e.g., using merkle proofs / SPV-style verification).
|
|
8
|
-
- **Completeness:** If a transaction depends on other transactions, the protocol recursively fetches all required inputs so the end result isn’t partial or broken.
|
|
9
|
-
- **Efficiency:** Nodes only sync what they don’t have, reducing duplicates and bandwidth.
|
|
10
|
-
- **Redundancy / availability:** Multiple nodes can converge on the same overlay view over time, improving uptime and reducing single-host dependency.
|
|
11
|
-
|
|
12
|
-
## Importance
|
|
13
|
-
|
|
14
|
-
Overlays are useful because they let you track only the topic you care about. But once an overlay is distributed (multiple nodes serving the same topic), nodes need a path to **catch up** and **stay consistent** over time.
|
|
15
|
-
|
|
16
|
-
GASP matters because it gives you:
|
|
17
|
-
|
|
18
|
-
- **Redundancy and uptime:** If one overlay host goes offline, other synced hosts can still serve the same topic, reducing single points of failure.
|
|
19
|
-
- **Fast bootstrap:** A fresh node can synchronize overlay state from peers instead of re-ingesting all previous transactions from scratch.
|
|
20
|
-
- **Lower-trust sync (for correctness):** Peers exchange transactions with proofs and recursively prove inputs, so received data can be validated instead of trusted blindly. *(This doesn’t replace your auth policy; it reduces trust needed for correctness.)*
|
|
21
|
-
- **Scaling the ecosystem:** As more apps and topics exist, GASP enables horizontal growth (more hosts for the same topic) instead of centralizing into one massive indexer.
|
|
22
|
-
|
|
23
|
-
## How GASP Works
|
|
24
|
-
|
|
25
|
-
One party initiates sync by summarizing what it currently has, then the peer responds with what’s missing, and both sides iterate until they converge.
|
|
26
|
-
|
|
27
|
-
A typical flow looks like:
|
|
28
|
-
|
|
29
|
-
1) **Summarize local state**
|
|
30
|
-
The initiator summarizes known spendable outpoints (commonly via a Bloom filter over TXID+VOUT).
|
|
31
|
-
|
|
32
|
-
2) **Responder sends “missing” inventory**
|
|
33
|
-
The responder identifies items the initiator likely doesn’t have and returns inventory entries containing the output/transaction plus proof material (and optional metadata).
|
|
34
|
-
|
|
35
|
-
3) **Recursive completion**
|
|
36
|
-
If the initiator is missing any input transactions required to validate what it received, it requests them. This repeats recursively until the needed graph is complete.
|
|
37
|
-
|
|
38
|
-
4) **Verification + finalize**
|
|
39
|
-
Anything that can’t be validated/anchored is ignored rather than partially imported.
|
|
40
|
-
|
|
41
|
-
## Usage
|
|
42
|
-
|
|
43
|
-
There are two main ways to activate and run GASP sync:
|
|
44
|
-
|
|
45
|
-
- **With CARS (cloud / managed):** Use CARS menus to enable sync options and deploy.
|
|
46
|
-
- **Without CARS (direct / self-managed):** Run your own Overlay Express server and trigger sync using the admin endpoints.
|
|
47
|
-
|
|
48
|
-
> **Important:** For GASP sync to work, *both* parties must have GASP enabled and must be configured to sync the same topic(s).
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
### With CARS
|
|
53
|
-
|
|
54
|
-
> If your project has a package.json script called `cars`, you can use `npm run cars`. Otherwise you typically run `cars` directly.
|
|
55
|
-
|
|
56
|
-
### Using CARS (high level)
|
|
57
|
-
|
|
58
|
-
- `npm run cars` *(or `cars`)*
|
|
59
|
-
- Manage Projects
|
|
60
|
-
- Edit Advanced Engine Config
|
|
61
|
-
- Choose correct CARS config
|
|
62
|
-
- Toggle `gaspSync`
|
|
63
|
-
- Edit `syncConfiguration`
|
|
64
|
-
- Add your topic manager name(s) (e.g. `tm_example`)
|
|
65
|
-
- Back → Done
|
|
66
|
-
- Back to main menu
|
|
67
|
-
- Build Artifact → Auto-create new release and upload latest artifact now
|
|
68
|
-
|
|
69
|
-
### Side notes
|
|
70
|
-
|
|
71
|
-
- Simply toggling `gaspSync` is not enough if your node is not “interested” in syncing your topic.
|
|
72
|
-
You must also ensure your topic manager name is included in `syncConfiguration` so the engine knows which topic(s) to sync.
|
|
73
|
-
|
|
74
|
-
---
|
|
75
|
-
|
|
76
|
-
### Without CARS
|
|
77
|
-
|
|
78
|
-
Without CARS, you run your own Overlay Express server and configure the overlay engine so it:
|
|
79
|
-
1) **discovers peers / publishes ads** (via an advertiser), and
|
|
80
|
-
2) knows **which topic managers to sync** (via `syncConfiguration`), and
|
|
81
|
-
3) has **GASP enabled**.
|
|
82
|
-
|
|
83
|
-
## Minimal engine config you must have
|
|
84
|
-
|
|
85
|
-
The most important lines are:
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
server.configureEngineParams({
|
|
89
|
-
advertiser: wa,
|
|
90
|
-
syncConfiguration: {
|
|
91
|
-
'tm_plite': 'SHIP',
|
|
92
|
-
'tm_blockbeta': 'SHIP',
|
|
93
|
-
},
|
|
94
|
-
logTime: false,
|
|
95
|
-
logPrefix: '[OVERLAY] ',
|
|
96
|
-
throwOnBroadcastFailure: false,
|
|
97
|
-
suppressDefaultSyncAdvertisements: true,
|
|
98
|
-
})
|
|
99
|
-
// ^ tells the engine which topic(s) you want to sync, and the discovery mechanism to use (e.g. SHIP)
|
|
100
|
-
|
|
101
|
-
server.configureEnableGASPSync(true)
|
|
102
|
-
// ^ enables GASP sync in Overlay Express
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
## **Overlay Express Setup Example**
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
import { WalletAdvertiser } from '@bsv/overlay-discovery-services'
|
|
109
|
-
import OverlayExpress from '@bsv/overlay-express'
|
|
110
|
-
import { config } from 'dotenv'
|
|
111
|
-
import packageJson from '../package.json'
|
|
112
|
-
import PollrTopicManager from './services/pollroverlay/PollrTopicManager'
|
|
113
|
-
import PollrLookupServiceFactory from './services/pollroverlay/PollrLookupServiceFactory'
|
|
114
|
-
import ForumTopicManager from './services/blockitoverlay/ForumTopicManager'
|
|
115
|
-
import ForumLookupService from './services/blockitoverlay/ForumLookupServiceFactory'
|
|
116
|
-
|
|
117
|
-
config()
|
|
118
|
-
|
|
119
|
-
const main = async () => {
|
|
120
|
-
const server = new OverlayExpress(
|
|
121
|
-
process.env.NODE_NAME!,
|
|
122
|
-
process.env.SERVER_PRIVATE_KEY!,
|
|
123
|
-
process.env.HOSTING_URL!,
|
|
124
|
-
process.env.ADMIN_TOKEN! // your chosen admin token to use the admin API
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
const wa = new WalletAdvertiser(
|
|
128
|
-
process.env.NETWORK! as 'main' | 'test',
|
|
129
|
-
process.env.SERVER_PRIVATE_KEY!,
|
|
130
|
-
process.env.WALLET_STORAGE_URL!,
|
|
131
|
-
process.env.HOSTING_URL!
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
await wa.init()
|
|
135
|
-
|
|
136
|
-
server.configureEngineParams({
|
|
137
|
-
advertiser: wa,
|
|
138
|
-
syncConfiguration: {
|
|
139
|
-
'tm_plite': 'SHIP',
|
|
140
|
-
'tm_blockbeta': 'SHIP',
|
|
141
|
-
},
|
|
142
|
-
logTime: false,
|
|
143
|
-
logPrefix: '[OVERLAY] ',
|
|
144
|
-
throwOnBroadcastFailure: false,
|
|
145
|
-
suppressDefaultSyncAdvertisements: true,
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
server.configureArcApiKey(process.env.ARC_API_KEY!)
|
|
149
|
-
server.configurePort(8080)
|
|
150
|
-
|
|
151
|
-
await server.configureKnex(process.env.KNEX_URL!)
|
|
152
|
-
await server.configureMongo(process.env.MONGO_URL!)
|
|
153
|
-
|
|
154
|
-
server.configureTopicManager('tm_plite', new PollrTopicManager())
|
|
155
|
-
server.configureLookupServiceWithMongo('ls_plite', PollrLookupServiceFactory)
|
|
156
|
-
|
|
157
|
-
server.configureTopicManager('tm_blockbeta', new ForumTopicManager())
|
|
158
|
-
server.configureLookupServiceWithMongo('ls_blockbeta', ForumLookupService)
|
|
159
|
-
|
|
160
|
-
server.configureEnableGASPSync(true)
|
|
161
|
-
|
|
162
|
-
await server.configureEngine()
|
|
163
|
-
|
|
164
|
-
server.app.get('/version', (req, res) => res.json(packageJson))
|
|
165
|
-
|
|
166
|
-
await server.start()
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
main()
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
# **Validation**
|
|
173
|
-
|
|
174
|
-
A practical way to validate sync is to compare per-topic records in your SQL database before and after sync.
|
|
175
|
-
|
|
176
|
-
Example (adapt the table/query to your schema):
|
|
177
|
-
### **Before sync**
|
|
178
|
-
|
|
179
|
-
```
|
|
180
|
-
+-------------+----+
|
|
181
|
-
| topic | n |
|
|
182
|
-
+-------------+----+
|
|
183
|
-
| tm_plite | 2 |
|
|
184
|
-
| tm_blockbeta| 1 |
|
|
185
|
-
| tm_ship | 2 |
|
|
186
|
-
| tm_slap | 2 |
|
|
187
|
-
+-------------+----+
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### **After sync**
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
+-------------+------+
|
|
194
|
-
| topic | n |
|
|
195
|
-
+-------------+------+
|
|
196
|
-
| tm_plite | 10 |
|
|
197
|
-
| tm_blockbeta| 17 |
|
|
198
|
-
| tm_ship | 2177 |
|
|
199
|
-
| tm_slap | 2 |
|
|
200
|
-
+-------------+------+
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
**What you’re looking for:** the topic(s) you care about (e.g. tm_plite, tm_blockbeta) should move toward the same counts/data across nodes after sync, and your lookup results for identical queries should converge as well.
|
|
204
|
-
|
|
205
|
-
> **Caveat (BRC-136 / janitor / admin removal):** Row-count comparison is only a
|
|
206
|
-
> valid convergence check on nodes that do **not** remove outputs locally. Once
|
|
207
|
-
> the janitor or `/admin/remove-token` is active, lookup-set counts can
|
|
208
|
-
> legitimately differ between peers even when they fully agree on what was
|
|
209
|
-
> admitted, because bans/removals are per-node policy and are not synchronized.
|
|
210
|
-
> To verify that two nodes agree on which transactions were admitted to a topic,
|
|
211
|
-
> compare their per-topic Topic Anchor Chain (`TAC`) at the tip rather than
|
|
212
|
-
> lookup row counts. See [BRC-136 BASM](./BRC-136-BASM.md).
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Overlay Use and Optimization in the BSV Ecosystem
|
|
2
|
-
|
|
3
|
-
[🏠 Home](../README.md) | [📚 API](../API.md) | [💡 Concepts](./README.md) | [📖 Examples](../examples/README.md) | [⚙️ Internal](../internal/README.md)
|
|
4
|
-
|
|
5
|
-
**Navigation:** [Overview](./00-overview.md) | [Best Practices](./01-best-practices.md) | [Query Performance](./02-query-performance.md) | [Database Monitoring](./03-database-monitoring.md) | [Pagination Example](./04-pagination-example.md) | [Recommendations Summary](./05-recommendations-summary.md)
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Introduction
|
|
10
|
-
|
|
11
|
-
Overlays are distributed application layers built on top of Bitcoin SV (BSV). They allow applications to publish structured data and then query it efficiently through **Lookup Services** and **Topic Managers**. This pattern enables developers to build scalable, high‑performance, privacy‑aware applications without needing to index or scan the entire blockchain themselves.
|
|
12
|
-
|
|
13
|
-
Applications such as **Convo Messenger**, **Tempo**, and **MetaMarket** use overlays to organize messages, content references, metadata, and other application‑specific data in a predictable and queryable way.
|
|
14
|
-
|
|
15
|
-
This documentation is written for **developers who want to build overlays for their own applications**. It explains how overlays work, what must be stored, how to structure queries, and how to keep overlay implementations efficient and maintainable.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Purpose of This Documentation
|
|
20
|
-
|
|
21
|
-
Developers implementing overlays commonly need guidance on:
|
|
22
|
-
|
|
23
|
-
* What data a Lookup Service should store
|
|
24
|
-
* How to design efficient queries for retrieving overlay data
|
|
25
|
-
* How to index and structure Lookup Service databases
|
|
26
|
-
* How to keep overlays healthy and performant as they scale
|
|
27
|
-
|
|
28
|
-
This documentation focuses on **practical guidance** that helps developers design overlays correctly and avoid common pitfalls. Examples from real applications—such as Convo Messenger—are used when relevant.
|
|
29
|
-
|
|
30
|
-
This documentation does **not** cover internal BSVA system design, Overlay Express internals, or future platform development proposals.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## What This Series Covers
|
|
35
|
-
|
|
36
|
-
Each document in this series focuses on a key aspect of overlay implementation.
|
|
37
|
-
|
|
38
|
-
### 1. Best Practices for Overlay Design
|
|
39
|
-
|
|
40
|
-
* What Lookup Services are required to store
|
|
41
|
-
* What additional metadata overlays often include
|
|
42
|
-
* What kinds of data should *not* be stored (e.g., large payloads)
|
|
43
|
-
* How to use UHRP references for external content
|
|
44
|
-
|
|
45
|
-
### 2. Query Optimization and Performance
|
|
46
|
-
|
|
47
|
-
* How to design efficient queries
|
|
48
|
-
* How to choose indexes for common access patterns
|
|
49
|
-
* How to avoid unbounded or slow query patterns
|
|
50
|
-
|
|
51
|
-
### 3. Monitoring and Debugging Overlays
|
|
52
|
-
|
|
53
|
-
* How to monitor your own overlay deployment
|
|
54
|
-
* How to inspect stored data safely
|
|
55
|
-
* How to track ingestion issues from your Topic Manager
|
|
56
|
-
|
|
57
|
-
### 4. Pagination and Practical Examples
|
|
58
|
-
|
|
59
|
-
* Why pagination is critical for performance
|
|
60
|
-
* How to implement pagination in Lookup Services and clients
|
|
61
|
-
* Examples drawn from live overlay behavior (e.g., Convo Messenger)
|
|
62
|
-
|
|
63
|
-
### 5. Summary of Recommendations
|
|
64
|
-
|
|
65
|
-
* A concise reference of best practices
|
|
66
|
-
* Quick reminders for developers building overlays
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Deliverables
|
|
71
|
-
|
|
72
|
-
This documentation includes:
|
|
73
|
-
|
|
74
|
-
* **Markdown files** that explain how to build, index, and monitor overlay implementations
|
|
75
|
-
* **Examples** demonstrating common patterns such as pagination and indexed lookup queries
|
|
76
|
-
* **Guidelines** that help ensure overlays remain scalable and consistent across different applications
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
## Next Steps
|
|
81
|
-
|
|
82
|
-
1. Read the general best practices for overlay storage (`01-best-practices.md`).
|
|
83
|
-
2. Review query performance and indexing fundamentals (`02-query-performance.md`).
|
|
84
|
-
3. Explore examples using Convo Messenger (`04-pagination-example.md`).
|
|
85
|
-
4. Refer to the summary document for quick reminders or cross-references.
|