mirror of
https://github.com/PR0M3TH3AN/bitlogin.git
synced 2026-10-27 08:31:21 +00:00
Relay lists: drop a relay that cannot accept writes, widen for margin
Measured every candidate on 2026-08-05 against its own NIP-11 document and over a real socket, three runs each. nostr.wine advertises payment_required AND restricted_writes, so it can never accept a free user's capsule -- yet it held one of five slots in BUILTIN_VAULT_RELAYS, the list capsules are PUBLISHED to. relay.damus.io and relay.nostr.band were unreachable 3/3 from the machine under test. That left two usable relays against publishAndVerify's floor of two acknowledgements and two readbacks: zero margin, so a single hiccup fails registration or a password rotation outright. Vault list is now eight relays, each verified to accept anonymous writes (no payment_required, no auth_required, no restricted_writes). damus stays for reach -- its failure here may be datacenter-IP filtering rather than downtime -- but quorum no longer depends on it. Discovery gains primal; relay.nostr.band stays there, where a search relay belongs, rather than in a write path. Pinned by hermetic tests: no WRITE_HOSTILE_RELAYS entry may appear in the vault list, the list must keep margin over the floor, and both lists must be duplicate-free wss URLs. Reachability is a property of the user's network and is deliberately NOT asserted; write policy is a property of the relay and is. Mutation-verified: putting nostr.wine back turns the pin red. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
1 parent
30b4f34300
commit
5aa7d9ca15
2 files changed
+87
-2
No files matched your search
@@ -23,9 +23,56 @@ export const MAINTAINER_PUBLIC_KEY_HEX: string | null = null;
|
||||
// comfortably -- a 3-relay list has zero margin the moment one relay is
|
||||
// unreachable, which is exactly what "add more vault relays" (the
|
||||
// registration failure message) is telling operators to fix.
|
||||
export const BUILTIN_VAULT_RELAYS: readonly string[] = ["wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band", "wss://nostr.wine", "wss://relay.snort.social"];
|
||||
/**
|
||||
* Relays that CANNOT accept an ordinary user's capsule, by their own policy.
|
||||
*
|
||||
* Measured 2026-08-05 from each relay's NIP-11 document, not from reputation:
|
||||
* `nostr.wine` advertises `payment_required: true` and
|
||||
* `restricted_writes: true`, so a free user's write is refused there no matter
|
||||
* how healthy the socket looks. It sat in the vault list below occupying one
|
||||
* of five write slots it could never fill.
|
||||
*
|
||||
* A relay here may still be perfectly good for READING. This list exists so
|
||||
* the test suite can refuse to let one back into a write path.
|
||||
*/
|
||||
export const WRITE_HOSTILE_RELAYS: readonly string[] = ["wss://nostr.wine", "wss://nostr21.com", "wss://nostr.land"];
|
||||
|
||||
export const BUILTIN_DISCOVERY_RELAYS: readonly string[] = ["wss://purplepag.es", "wss://relay.nostr.band", "wss://nostr-pub.wellorder.net"];
|
||||
/**
|
||||
* Vault relays: where capsules are PUBLISHED, so every entry must accept
|
||||
* anonymous writes.
|
||||
*
|
||||
* Widened 2026-08-05 after measuring the old five. Only two could actually
|
||||
* take a write from a fresh client: nostr.wine refuses by policy (above), and
|
||||
* relay.damus.io and relay.nostr.band were unreachable across three runs from
|
||||
* the machine under test. Two usable relays against a floor of two
|
||||
* acknowledgements is zero margin -- one hiccup and registration or a
|
||||
* password rotation fails outright.
|
||||
*
|
||||
* damus stays: it is the most widely used relay on the network and the
|
||||
* failure may be datacenter-IP filtering rather than downtime. The point of
|
||||
* widening is that quorum no longer DEPENDS on it.
|
||||
*/
|
||||
export const BUILTIN_VAULT_RELAYS: readonly string[] = [
|
||||
"wss://nos.lol",
|
||||
"wss://offchain.pub",
|
||||
"wss://nostr.bitcoiner.social",
|
||||
"wss://relay.mostr.pub",
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.primal.net",
|
||||
"wss://relay.snort.social",
|
||||
"wss://relay.damus.io",
|
||||
];
|
||||
|
||||
/**
|
||||
* Discovery relays: read-only lookups (profiles, relay lists), so a
|
||||
* search-oriented or write-restricted relay is appropriate here.
|
||||
*/
|
||||
export const BUILTIN_DISCOVERY_RELAYS: readonly string[] = [
|
||||
"wss://purplepag.es",
|
||||
"wss://relay.nostr.band",
|
||||
"wss://nostr-pub.wellorder.net",
|
||||
"wss://relay.primal.net",
|
||||
];
|
||||
|
||||
/** Well-known static HTTPS fallback URLs for the signed relay-list document (§19.1). */
|
||||
export const BOOTSTRAP_HTTPS_FALLBACK_URLS: readonly string[] = [];
|
||||
|
||||
@@ -16,7 +16,10 @@ import {
|
||||
import { RelayPool } from "./pool.js";
|
||||
import { MockRelay } from "../test-support/mockRelay.js";
|
||||
import {
|
||||
BUILTIN_DISCOVERY_RELAYS,
|
||||
BUILTIN_VAULT_RELAYS,
|
||||
MAINTAINER_PUBLIC_KEY_HEX,
|
||||
WRITE_HOSTILE_RELAYS,
|
||||
mergeRelayLists,
|
||||
parseAndVerifyBootstrapList,
|
||||
type BootstrapRelayList,
|
||||
@@ -427,3 +430,38 @@ describe("RelayPool quorum", () => {
|
||||
pool.closeAll();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* The built-in relay lists are a shipped configuration that decides whether a
|
||||
* user can create an account at all, so they get the same treatment as any
|
||||
* other invariant: pinned, with the reasoning attached.
|
||||
*
|
||||
* Hermetic on purpose -- no relay is contacted here. Reachability is a
|
||||
* property of the network the user is on and cannot be asserted in CI;
|
||||
* WRITE POLICY is a property of the relay and can.
|
||||
*/
|
||||
describe("built-in relay lists", () => {
|
||||
it("keeps write-hostile relays out of the vault (publish) list", () => {
|
||||
// nostr.wine advertises payment_required + restricted_writes in its own
|
||||
// NIP-11 document, so it can never accept a free user's capsule. It sat
|
||||
// in this list occupying one of five write slots it could not fill.
|
||||
for (const hostile of WRITE_HOSTILE_RELAYS) {
|
||||
expect(BUILTIN_VAULT_RELAYS).not.toContain(hostile);
|
||||
}
|
||||
});
|
||||
|
||||
it("leaves enough margin over the publish floor to lose relays", () => {
|
||||
// publishAndVerify enforces minAcks/minReadbacks = 2 regardless of list
|
||||
// size. Two usable relays against a floor of two is zero margin: one
|
||||
// unreachable relay fails registration outright. Require room to lose
|
||||
// several and still clear the floor.
|
||||
expect(BUILTIN_VAULT_RELAYS.length).toBeGreaterThanOrEqual(6);
|
||||
});
|
||||
|
||||
it("ships no duplicates and only secure websocket URLs", () => {
|
||||
for (const list of [BUILTIN_VAULT_RELAYS, BUILTIN_DISCOVERY_RELAYS]) {
|
||||
expect(new Set(list).size).toBe(list.length);
|
||||
for (const url of list) expect(url.startsWith("wss://")).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user