floonet-strfry: whitelist the marketplace kind set

Extend the write-policy whitelist from the Goblin-wallet-only kinds to the
union with Magick Market so one relay serves both apps (default-deny for
everything else). Adds 1 note, 7 reaction, 14/16/17 order+receipt (Gamma),
1111 comment, 10000 mute/blacklist, 24133 remote signing, 30000/30003
NIP-51 sets, 30078 app data, 30402/30405/30406 listing/collection/shipping,
31990 handler info. Keeps the Goblin base including 13 seal and 27235
NIP-98. Plugin default, docstring, and tests updated together.
This commit is contained in:
2ro
2026-07-02 22:19:29 -04:00
parent 16302ed309
commit c5ca6860d7
2 changed files with 60 additions and 7 deletions
+41 -3
View File
@@ -27,11 +27,46 @@ their events with a `-` tag: first attempt triggers the challenge, the
client AUTHs, then republishes. Verified end to end against strfry
b80cda3a812af1b662223edad47eb70b053508b6.
This relay serves two apps, so the whitelist is the union of their kinds
(default-deny for everything else). Goblin wallet kinds:
0 profile metadata (NIP-01)
3 contact list (NIP-02)
5 event deletion (NIP-09)
13 seal: inner sealed event of a gift wrap (NIP-59)
1059 gift wrap: sealed DMs and payments (NIP-59)
10002 relay list metadata (NIP-65)
10050 DM relay list (NIP-17)
27235 HTTP auth event for the name authority (NIP-98)
Magick Market marketplace kinds (also reuses 0/5/1059/10002 above):
1 text note: bug reports, shared listings (NIP-01)
7 reaction (NIP-25)
14 order chat / general order message, plaintext (Gamma spec)
16 order processing and status update (Gamma spec)
17 payment receipt / confirmation (Gamma spec)
1111 comment (NIP-22)
10000 mute list, used as merchant/product blacklist (NIP-51)
30000 people set: admins, editors, featured users, vanity, NIP-05 (NIP-51)
30003 bookmark set: featured collections (NIP-51)
30078 app-specific data: cart, relay prefs, V4V (NIP-78)
30402 product listing (NIP-99)
30405 product collection / featured products (Gamma spec)
30406 shipping option (Gamma spec)
31990 handler information (NIP-89)
24133 NIP-46 remote signing (Nostr Connect, ephemeral — Goblin wallet login)
Excluded on purpose: 25910 (ContextVM) only ever rides inside a 1059 gift
wrap, never raw; 30017/30018 (legacy NIP-15) are read from sellers' own relays
during migration, never written here; 9735 (Lightning zap receipt) is dead in
this GRIN-only fork.
Configuration is environment variables (set them on the strfry process; the
plugin inherits them, e.g. via docker compose or the systemd unit):
FLOONET_ALLOWED_KINDS comma-separated kind whitelist
[default: 0,3,5,13,1059,10002,10050,27235]
FLOONET_ALLOWED_KINDS comma-separated kind whitelist [default: the
Goblin + Magick Market set documented above]
FLOONET_REQUIRE_AUTH true/false [default: false]
FLOONET_PAY_MODE off|name|write [default: off]
(only "write" changes plugin behavior; "name" is
@@ -53,7 +88,10 @@ import sys
import time
import urllib.request
DEFAULT_ALLOWED_KINDS = "0,3,5,13,1059,10002,10050,27235"
DEFAULT_ALLOWED_KINDS = (
"0,1,3,5,7,13,14,16,17,1059,1111,10000,10002,10050,24133,27235,"
"30000,30003,30078,30402,30405,30406,31990"
)
def load_config(env=os.environ):
+19 -4
View File
@@ -23,7 +23,10 @@ import floonet_writepolicy as wp
PLUGIN = os.path.join(os.path.dirname(os.path.abspath(__file__)), "floonet_writepolicy.py")
PK = "a" * 64
DEFAULT_KINDS = (0, 3, 5, 13, 1059, 10002, 10050, 27235)
DEFAULT_KINDS = (
0, 1, 3, 5, 7, 13, 14, 16, 17, 1059, 1111, 10000, 10002, 10050, 24133,
27235, 30000, 30003, 30078, 30402, 30405, 30406, 31990,
)
def req(kind, authed=None, event_id="e1"):
@@ -54,11 +57,23 @@ class KindWhitelist(unittest.TestCase):
self.assertEqual(reply["id"], "e1")
def test_disallowed_kinds_rejected(self):
for kind in (1, 4, 6, 7, 14, 1058, 1060, 30023, 22242, -1):
# 25910 (ContextVM) rides inside 1059 gift wraps only;
# 30017/30018 (legacy NIP-15) come from sellers' own relays;
# 9735 (zap) is dead in the GRIN-only fork. All stay rejected.
for kind in (4, 6, 9735, 1058, 1060, 25910, 30017, 30018, 30023, 22242, -1):
reply = wp.decide(req(kind), cfg())
self.assertEqual(reply["action"], "reject", "kind %d" % kind)
self.assertIn("kind not accepted", reply["msg"])
def test_marketplace_kind_accepted_and_zap_rejected(self):
# A newly-allowed Magick Market kind (NIP-89 handler info) is accepted.
self.assertEqual(wp.decide(req(31990), cfg())["action"], "accept")
# A still-rejected kind (Lightning zap receipt, disabled in the
# GRIN-only marketplace) is refused by the default-deny whitelist.
reply = wp.decide(req(9735), cfg())
self.assertEqual(reply["action"], "reject")
self.assertIn("kind not accepted", reply["msg"])
def test_malformed_kind_fails_closed(self):
for bad in (None, "1059", 3.5, True, [1059]):
r = req(0)
@@ -174,7 +189,7 @@ class PaidWriteGate(unittest.TestCase):
def test_kind_check_still_first_in_write_mode(self):
_Authority.paid_pubkeys = {PK}
reply = wp.decide(req(1, authed=PK), self.c())
reply = wp.decide(req(9735, authed=PK), self.c())
self.assertEqual(reply["action"], "reject")
self.assertIn("kind not accepted", reply["msg"])
@@ -199,7 +214,7 @@ class StrfryPipeProtocol(unittest.TestCase):
return [json.loads(out) for out in proc.stdout.splitlines()]
def test_accept_and_reject_over_the_wire(self):
replies = self.run_plugin([req(1059, event_id="ok1"), req(1, event_id="no1"), req(0, event_id="ok2")])
replies = self.run_plugin([req(1059, event_id="ok1"), req(9735, event_id="no1"), req(0, event_id="ok2")])
self.assertEqual(
[(r["id"], r["action"]) for r in replies],
[("ok1", "accept"), ("no1", "reject"), ("ok2", "reject" if 0 not in DEFAULT_KINDS else "accept")],