@@ -540,7 +540,7 @@ mapEach = \items getKey action -> case headOf items of
541-- Hex tokens: one random hex char per recursive call.
541-- Hex tokens: one random hex char per recursive call.
542hexDigit : Int -> Text
542hexDigit : Int -> Text
543−hexDigit = \n -> take 1 (drop (mod n 16) "0123456789abcdef")
543+hexDigit = \n -> take 1 (drop n "0123456789abcdef")
545genTokenN = \n -> if n <= 0 then yield "" else do
545genTokenN = \n -> if n <= 0 then yield "" else do
546 d <- randomInt 16
546 d <- randomInt 16
@@ -553,7 +553,7 @@ tooLong : Text -> Int -> Bool
553tooLong = \s maxLen -> count (chars s) > maxLen
553tooLong = \s maxLen -> count (chars s) > maxLen
555isValidHex : Text -> Bool
555isValidHex : Text -> Bool
556−isValidHex = \s -> let cs = chars s in count cs > 0 && mod (count cs) 2 == 0 && all cs (\c -> contains c "0123456789abcdef")
556+isValidHex = \s -> count (chars s) > 0 && mod (count (chars s)) 2 == 0 && all (chars s) (\c -> contains c "0123456789abcdef")
558normalizeHex : Text -> Text
558normalizeHex : Text -> Text
559normalizeHex = \s -> bytesToHex (bytesFromHex s)
559normalizeHex = \s -> bytesToHex (bytesFromHex s)
@@ -574,8 +574,7 @@ hasPrefix = \pre s -> take (count (chars pre)) s == pre
575containsSuffix : Text -> Text -> Bool
575containsSuffix : Text -> Text -> Bool
576containsSuffix = \suffix s ->
576containsSuffix = \suffix s ->
577− let d = count (chars s) - count (chars suffix) in
577+ drop (max 0 (count (chars s) - count (chars suffix))) s == suffix
578− d >= 0 && drop d s == suffix
580-- Block private/reserved/internal hostnames (SSRF defense). Skips obscure encoding
579-- Block private/reserved/internal hostnames (SSRF defense). Skips obscure encoding
581-- tricks (octal, hex, percent-encoding) but covers the high-value cases.
580-- tricks (octal, hex, percent-encoding) but covers the high-value cases.
@@ -826,9 +825,8 @@ knownPeerServersFromPresence = \presence -> collectPeers presence (\p -> p.serve
826-- Last-write-wins merge by (pubkey, server), keeping the entry with the larger expiresAt.
825-- Last-write-wins merge by (pubkey, server), keeping the entry with the larger expiresAt.
827mergePresence = \existing incoming ->
826mergePresence = \existing incoming ->
828 fold (\acc e ->
827 fold (\acc e ->
829− let same = \x -> x.pubkey == e.pubkey && x.server == e.server in
830− if any acc (\x -> same x && x.expiresAt >= e.expiresAt) then acc
828+ if any acc (\x -> x.pubkey == e.pubkey && x.server == e.server && x.expiresAt >= e.expiresAt) then acc
831− else union (removeWhere acc same) [e]) existing incoming
829+ else union (removeWhere acc (\x -> x.pubkey == e.pubkey && x.server == e.server)) [e]) existing incoming
833-- ============================================
831-- ============================================
834-- Pruning
832-- Pruning