Purpose: Discover listings, parse metadata, filter by payment method / asset / amount, cache results.
Complexity: Low — Nostr subscription (NIP-99) + JSON parsing + a local cache.
⚠️ Phase 0 Priority - Essential for Mainnet Launch
The bulletin board is a fundamental gear of Asgaya and critical to our compliance strategy:
Why Phase 0 (High Priority):
- Sender discovery - Senders find BCH sellers to fund covenants (no bulletin board = no way to create remittances)
- Recipient cashout - Recipients find nearby merchants to cash out BCH (essential for completing remittance flow)
- Compliance requirement - Asgaya helps users discover each other, not intermediates transactions. Bulletin board proves we’re a discovery tool, not a money transmitter.
Without this component: Mainnet launch impossible. Users can’t find counterparties.
Implementation status (2026-09-20): 🟢 Phase-0 board shipped. Discovery is Nostr — signed, self-published listings (NIP-99,
kind:30402) cached locally. A mock source ships for development; the on-chain anchor is Phase 0+ (planned).Known limitation (2026-09-16) — discovery: our Electrum/Fulcrum server cannot enumerate listings by token category (verified:
blockchain.nft.list_categoryunsupported; only scripthash-scoped queries work). That is why Phase-0 discovery is Nostr — the on-chain query arrives with the planned anchor/explorer (Phase 0+).
The Bulletin Board is how users discover counterparties:
Every listing carries the poster’s BCH pubkey (builds the funding covenant) and their Nostr pubkey (npub) — the coordination address — so a counterparty can open the encrypted channel (Nostr) without any other contact information.
No centralized server: listings are signed Nostr events (NIP-99, kind:30402); each device stores the ones it sees and filters locally. (The on-chain anchor — a censorship-resistant copy of the essential fields — is Phase 0+; see below.)
kind:30402)Addressable — an update replaces the previous version — and signed by the poster’s npub. It carries the listing two ways:
d (stable ad id), t (asgaya-board, asgaya-seller/asgaya-buyer, asset:<slug>, pay:<slug>), bchpub, fee, buffer, location, min, max, hours, cash, status.content JSON (canonical, authoritative payload).bchpub — the poster’s BCH pubkey (the covenant funder).npub (the coordination key).npub is the x-coordinate of the BCH identity key, so a client can verify the binding offline.Publish/replace the same d → the relay replaces the prior version. Set status: paused to hide an ad without deleting it.
| Field | Meaning |
|---|---|
adType |
SELLER (sells BCH) or BUYER (sells an asset for BCH) |
assets |
non-BCH side: fiat code (EUR, VES), token (H€, HAu), or free text |
paymentMethods |
the fiat rail (Bizum, Cash in person, Card in person, …) — the effective filter |
bchPubkey / npub |
identity (funding key / coordination key) |
fee |
the poster’s own fee/rate (Phase-0 bootstrap default ~0.5%) |
buffer |
volatility buffer (minPricePercent, e.g. 93 = 7%) |
min / max |
amount bracket |
location |
required for cash-in-person |
hours / status |
availability / online |
| Ad | Posted by | Meaning | Used by |
|---|---|---|---|
Seller ad (SELLER) |
someone with BCH | “I sell BCH for fiat” | the sender (buying BCH for a remittance) |
Buyer ad (BUYER) |
a merchant / BCH buyer | “I sell ⟨asset⟩ for BCH” | the recipient (cashing out) |
These two labels are internal and derived — every poster is a seller of an asset; the board just resolves which side is BCH. The UI treats them as one thing: “I sell ⟨asset⟩, I accept ⟨…⟩.”
The recipient’s cash-out picker shows buyer ads; the sender’s picker shows seller ads. Both carry bchpub + npub.
The payment method is the filter. A recipient who picks “Cash in person” sees only the ads that buy BCH for cash; the sender filters by Bizum / SEPA. The asset is a multi-value field and is asset-agnostic — the app ships an
OTHERfree-text escape hatch (the “Other” input, Phase 0), so any asset can be listed; a protocol-level bitmask for the long tail is Phase 0+.
Your own ad is on the board like anyone else’s. The app therefore never offers your own ad as a counterparty — it matches the acting wallet against the listing’s wallet (BCH key or npub). This prevents user mistakes and self-dealing, which would fake trade volume and poison reputation data.
Pseudocode:
function publishListing(adType, assets, paymentMethods, fee, buffer, min, max, location):
listing = {
id: uuid(), // stable `d` — reused on update
adType: adType,
assets: assets,
paymentMethods: paymentMethods,
fee: fee, buffer: buffer,
min: min, max: max, location: location,
bchPubkey: myBchPubkey(), // identity / funder
npub: myNpub() // coordination (also the event signer)
}
event = nip99_event(
kind: 30402,
tags: buildTags(listing), // d, t:asgaya-board, t:asgaya-<type>, t:asset:*, t:pay:*, bchpub, …
content: json(listing)
)
event.sign(myNpub())
broadcast(event, relays) // no on-chain transaction in Phase 0
cacheLocally(listing)
return listing.id
Cost: a relay publish (free). Visibility: seconds.
One subscription, two filters — the app sends a single REQ carrying the DM filter and the board filter together:
{
"kinds": [30402],
"#t": ["asgaya-board"]
}
Each returned event is cached locally (keyed by author + d, so a newer version replaces the old); the pickers then read from the cache. No category lookup, no Electrum enumeration.
Note: relays only index single-letter tags, so filtering happens client-side over the cached set — fine for Phase 0’s scale, and it keeps the app index-agnostic (a future on-chain index slots in behind the same listing source).
Why client-side? No backend server, full privacy.
Filters:
[min, max] doesn’t include María’s amount.The ranking then orders the survivors — see Seller Ranking Algorithm.
board_events (discovered ads, raw event JSON keyed by author + d) and listings (your own ads).Nostr relays are censorable. Phase 0+ adds a per-seller NFT on Bitcoin Cash carrying the discovery-critical fields (identity, asset, payment_methods, fee), so listings survive relay censorship and can one day be enumerated directly on-chain.
Then the model is: find a listing via any index (Nostr or on-chain), verify it against the anchor — “index = find, chain = trust.” This is not required for Phase 0 and is tracked as Phase-0+ work.
contact-exchange).Listing round-trip (tags + content), filtering logic, addressable replace.nak (damus/snort) during Phase 0.Used by:
Interacts with:
Status: ✅ Phase 0 shipped (Nostr discovery); on-chain anchor = Phase 0+ (planned) Updated: 2026-09-21 Complexity: Low (relay publish + local cache + client-side filtering) Priority: Essential for discovery layer (sender → seller, recipient → merchant) Compliance: Proves Asgaya is discovery tool, not intermediary
| 🏠 Home | ↑ Android App | 📖 Glossary |