Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
package-lock.json
package-lock.json
strfry-db
13 changes: 13 additions & 0 deletions compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: "3.8"

services:
strfry:
image: strfry-custom
container_name: strfry-custom
ports:
- 7777:7777
volumes:
- ./strfry-db:/app/strfry-db
- ./strfry.conf:/etc/strfry.conf
restart: always
stop_grace_period: 2m
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0",
"description": "",
"main": "whitelist.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down
4 changes: 2 additions & 2 deletions strfry.conf
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ writePolicy {

negentropy {
# Support negentropy protocol messages
enabled = true
enabled = false

# Maximum records that sync will process before returning an error
maxSyncEvents = 1000000
}
}
}
124 changes: 78 additions & 46 deletions whitelist.js
Original file line number Diff line number Diff line change
@@ -1,87 +1,119 @@
#!/usr/bin/env node

const axios = require("axios");
const nostrTools = require("nostr-tools");
import axios from "axios";
import * as nostrTools from "nostr-tools";
import readline from "readline";

const GRAPHQL_URL = "https://api.flashapp.me/graphql"; // Replace with your actual GraphQL endpoint
const GRAPHQL_URL = "https://api.flashapp.me/graphql";

const rl = require("readline").createInterface({
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
});

const checkWhitelist = async (npub) => {
let encodedNpub = nostrTools.nip19.npubEncode(npub);
const query = `
query Query($input: IsFlashNpubInput!) {
isFlashNpub(input: $input) {
isFlashNpub
const checkWhitelist = async (hexPubkey) => {
try {
const npub = nostrTools.nip19.npubEncode(hexPubkey);

const query = `
query Query($input: IsFlashNpubInput!) {
isFlashNpub(input: $input) {
isFlashNpub
}
}
}
`;
`;

const variables = {
input: { npub: encodedNpub },
};
const variables = {
input: { npub },
};

console.error(
"Making API request with variables:",
JSON.stringify(variables)
); // Log request to stderr
console.error("Checking whitelist:", JSON.stringify(variables));

try {
const response = await axios.post(
GRAPHQL_URL,
{
query,
variables,
},
{
headers: { "Content-Type": "application/json" },
}
{ query, variables },
{ headers: { "Content-Type": "application/json" } },
);
console.error("API response:", response.data); // Log response to stderr
return response.data.data.isFlashNpub.isFlashNpub;
} catch (error) {
console.error("Error fetching whitelist status:", error.message);
console.error("Request variables were:", JSON.stringify(variables));

return Boolean(response?.data?.data?.isFlashNpub?.isFlashNpub);
} catch (err) {
console.error("Whitelist check failed:", err.message);
return false;
}
};

rl.on("line", async (line) => {
let req;
const res = {};

try {
req = JSON.parse(line);
} catch (error) {
console.error("Invalid JSON format");
} catch {
console.log(JSON.stringify({ action: "reject", msg: "invalid JSON" }));
return;
}

if (!req.event?.id) {
console.log(JSON.stringify({ action: "reject", msg: "missing event id" }));
return;
}

res.id = req.event.id;

if (req.type !== "new") {
console.error("unexpected request type");
res.action = "reject";
res.msg = "unexpected request type";
console.log(JSON.stringify(res));
return;
}

const npub = req.event.tags.filter((t) => t[0] === "p")[0]?.[1];
console.error("npub is", npub, "Request event is", req.event);
if (!npub) {
console.error("No referenced npub");
const { kind, pubkey, tags = [] } = req.event;

console.error("Processing event:", { id: res.id, kind, pubkey });

// kind 0, 1 & 3 → author must be whitelisted
if (kind === 0 || kind === 1 || kind === 3) {
if (!pubkey) {
res.action = "reject";
res.msg = "missing pubkey";
} else if (await checkWhitelist(pubkey)) {
res.action = "accept";
} else {
res.action = "reject";
res.msg = "blocked: author not on whitelist";
}

console.log(JSON.stringify(res));
return;
}

// Check if the pubkey is on the whitelist via GraphQL query
const isWhitelisted = await checkWhitelist(npub);
// kind 1059 → any p-tag must be whitelisted
if (kind === 1059) {
const pTags = tags.filter((t) => t[0] === "p" && t[1]).map((t) => t[1]);

if (!pTags.length) {
res.action = "reject";
res.msg = "missing p tag";
console.log(JSON.stringify(res));
return;
}

for (const hex of pTags) {
if (await checkWhitelist(hex)) {
res.action = "accept";
console.log(JSON.stringify(res));
return;
}
}

let res = { id: req.event.id }; // echo event's id
if (isWhitelisted) {
res.action = "accept";
} else {
res.action = "reject";
res.msg = "blocked: not on white-list";
res.msg = "blocked: no recipient on whitelist";
console.log(JSON.stringify(res));
return;
}

res.action = "reject";
res.msg = `unsupported kind ${kind}`;
console.log(JSON.stringify(res));
});