kind 35128 · don't trust, verify
7d57b1dc3176a3a524590eb84c0963cd6edc99b5eb63126b8bd94d1cd9f56b5a
{
"id": "7d57b1dc3176a3a524590eb84c0963cd6edc99b5eb63126b8bd94d1cd9f56b5a",
"pubkey": "c2f1ab4e6a577dd8980b57e15986ed92830b95a94f374192235563532b5cac20",
"created_at": 1791845194,
"kind": 35128,
"tags": [
[
"path",
"/index.html",
"f6e7a486c7415a60b61c04278ff5e1d11e579f191b49ac2878cf0513f4f3e7e8"
],
[
"path",
"/__MACOSX/._index.html",
"00955c70e38aafd2f20efca49ba95c1c3c5641f00b6eb868184bae27d5ada165"
],
[
"path",
"/404.html",
"f6e7a486c7415a60b61c04278ff5e1d11e579f191b49ac2878cf0513f4f3e7e8"
],
[
"server",
"https://nsite.run"
],
[
"server",
"https://nostr.download"
],
[
"server",
"https://blssm.us"
],
[
"server",
"https://blossom.yakihonne.com"
],
[
"server",
"https://zencrown.relay2aeon.quest/"
],
[
"relay",
"wss://nsite.run"
],
[
"relay",
"wss://nostr.bitcoiner.social"
],
[
"relay",
"wss://purplepag.es"
],
[
"relay",
"wss://relay.primal.net"
],
[
"relay",
"wss://zencrown.relay2aeon.quest"
],
[
"d",
"bbs-nostr"
],
[
"title",
"BBSNostr \u2014 a dial-up bulletin board for Nostr, in one nostr napplet "
],
[
"description",
"What it is\nBBSNostr is a single-file Nostr client that looks and feels like a 1990s bulletin board system. Relays are phone lines. Your key is your callsign. Posts are kind:1 events. Boards are t-tagged feeds. Doors are sandboxed mini-apps. There is no build step, no server, no install, no dependency manager. You open index.html and you are on the BBS.\n\nHow to run it\nSave the file as index.html and open it from file:// or drop it on any static host. The only network call is a single pinned import of nostr-tools from esm.sh at first paint. After that the browser caches it. Everything else \u2014 CSS, JS, doors, terminal chrome \u2014 is inline.\n\nFirst boot\nYou will be asked to set a callsign. Three ways: generate a fresh keypair (recommended), import an existing nsec, or attach a NIP-07 browser extension if one is installed. Generated keys are encrypted with your passphrase using PBKDF2-SHA-256 at 200k iterations and AES-GCM before they touch localStorage. If you skip the passphrase, you write the nsec down and the plaintext key is never persisted.\n\nThe relay pool\nBBSNostr ships a candidate registry of 72 public relays. At any moment exactly six are dialed and open. The pool scores every relay by successful reads, successful writes, rate-limit rejections, blocks, invalid events, and timeouts. Every 60 seconds it decays scores, fills empty slots with the best available candidate, and swaps out the worst active relay when a better one beats it by 15 points. A rejection that drops a relay below -15 triggers an immediate forced swap. Subscriptions are replayed against any relay that comes online mid-session, so a rotation never drops a board. Health persists to localStorage so the next session starts smarter than the last.\n\nPhone Book\nThe Phone Book screen is the pool dashboard. The top table lists the six active relays with their state, score, and OK-to-error ratio. You can hang up on any of them. The second table lists the top 30 inactive room candidates sorted by score; DIAL promotes one immediately and. dem Youotes the can worst active relay to make add your own relay URL, force a rotation, or reset every score to zero.\n\nBoards and posts\nEight curated boards are registered by default: #nostr, #bitcoin, #art, #dev, #music, #food, #retro, #help. You can add up to eight more. Opening a board fires a REQ for kind:1 events tagged with that slug against every active read relay. Each post card shows MSG# (first four characters of the event id), the board, a time-ago stamp, the author's display name if their kind:0 profile has been seen, the subject line if one was set, and the content. Quote lines beginning with > render dim. nostr: URIs are clickable and copy to clipboard.\n\nComposing\nPress C on a board to compose. Optional NIP-14 subject. Live character counter that turns amber at 3500 and red at 3950. When you hit post, the client signs the event locally, verifies the signature with the same library, and only then publishes to every active write relay. Each relay reports back with an OK frame and you see per-relay receipts inline. A relay that times out is marked as such. Nothing is sent unverified.\n\nReplies, reactions, reposts, deletes\nReplies use proper NIP-10 tags: a root marker, a reply marker, and a p tag on the parent author. Reactions are kind:7 events with the heart button; the count is live. Reposts are kind:6 events with the boost button. Your own posts carry a DELETE button that publishes a kind:5 request; relays may or may not honor it, which the UI acknowledges before you send.\n\nProfiles\nClick any author name to open a profile screen. If a kind:0 event for that pubkey is in the store, its name, about text, and nip05 appear. Their npub and hex pubkey are both copyable. Their recent posts across every board you have visited show up below.\n\nDoors\nDoors are sandboxed mini-apps that run inside a srcdoc iframe. Only one is alive at a time. The iframe is sandboxed with allow-scripts only \u2014 no same-origin, no forms, no top navigation. It talks to the parent through a strict postMessage envelope that checks both the source window and a per-instance UUID. Three doors ship: ASCII PAINT (draws a 40x20 canvas and exports it as an #art post), GUESTBOOK (reads the current board and signs a message), and NOSTR DICE (commit-reveal randomness across two signed events). Doors never see your nsec. They can only ask the parent to sign a template, and the parent prompts you before doing it.\n\nCommand palette\nCtrl+K opens a fuzzy command palette. Type to filter. Press a number to jump. Enter runs the first match. It covers every navigation command, refresh, compose, and forced relay rotation.\n\nKeyboard\n1-9 jump to numbered items. Enter selects. Esc goes back. ? toggles help. / focuses the board search. C composes. R refreshes. Ctrl+K opens the palette. Ctrl+L opens the sysop log. Ctrl+Enter posts from the composer. Q on the main menu logs off and clears your identity from the device.\n\nStorage\nEvery key is namespaced under bbsnostr:v1:. Identity, relays, boards, relay health, and unread markers each have their own validator. A corrupt value is dropped and the slice resets to defaults rather than crashing. When the whole namespace approaches 800 KB, the least important slice is evicted first, and identity is never a candidate.\n\nWhat it deliberately does not do\nIt does not use eval, new Function, or document.write. It does not use inline event handlers. It does not pass untrusted strings to innerHTML. It does not open more than six sockets. It does not store your raw private key when you chose a passphrase. It does not send telemetry anywhere; the sysop log is an in-memory ring buffer of 200 entries, visible only to you. It does not accept an event whose signature fails verification.\n\nAttribution\nThe protocol belongs to the NIP authors. BBSNostr is a client, and a nostalgic one at that. Everything it does on the wire is a NIP or a convention the network already speaks."
],
[
"client",
"nsite.run"
]
],
"content": "",
"sig": "727fb88edcc053b186b4ae8a59cf40fd752b63db468c0350c54eeb5a9dbb974b46ed99967a7c45e9efc59e5f10728e0096266eacacdaf2eec487142d6566d367"
}