Restrict public swarm participation and propagation to real public ICANN domains

Hop-State: A_06FP8GM0PKJTMF45B7016J0
Hop-Proposal: R_06FP8GKFXXZG84ETVCPS5ZG
Hop-Task: T_06FP8F45T3G4Z4ESW9NZQQR
Hop-Attempt: AT_06FP8F45T3QS81M2ZM4SJ3G
This commit is contained in:
2026-07-14 22:31:47 -07:00
committed by Hop
parent 988bd8ab38
commit 1427df4bdc
13 changed files with 282 additions and 61 deletions
+16 -1
View File
@@ -12,6 +12,7 @@ import { upsertSwarmNode } from '@/lib/swarm/registry';
import { buildAnnouncement } from '@/lib/swarm/discovery';
import { verifySwarmRequest } from '@/lib/swarm/signature';
import type { SwarmNodeInfo } from '@/lib/swarm/types';
import { getPublicSwarmDomain, isPublicSwarmDomain } from '@/lib/swarm/node-domain';
const optionalUrlSchema = z.preprocess((value) => {
if (typeof value !== 'string') {
@@ -55,9 +56,23 @@ export async function POST(request: Request) {
const data = signedAnnouncementSchema.parse(body);
const ourDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN;
if (!isPublicSwarmDomain(ourDomain)) {
return NextResponse.json(
{ error: 'This node is not configured for public swarm participation' },
{ status: 503 }
);
}
if (!isPublicSwarmDomain(data.domain)) {
return NextResponse.json(
{ error: 'Swarm nodes must use a public ICANN domain' },
{ status: 400 }
);
}
// Don't process announcements from ourselves
if (data.domain === ourDomain) {
if (getPublicSwarmDomain(data.domain) === getPublicSwarmDomain(ourDomain)) {
return NextResponse.json(
{ error: 'Cannot announce to self' },
{ status: 400 }
+16 -1
View File
@@ -12,6 +12,7 @@ import { processGossip } from '@/lib/swarm/gossip';
import { markNodeSuccess } from '@/lib/swarm/registry';
import { verifySwarmRequest } from '@/lib/swarm/signature';
import type { SwarmGossipPayload } from '@/lib/swarm/types';
import { getPublicSwarmDomain, isPublicSwarmDomain } from '@/lib/swarm/node-domain';
const handleSchema = z.object({
handle: z.string(),
@@ -61,9 +62,23 @@ export async function POST(request: Request) {
const data = signedGossipSchema.parse(body);
const ourDomain = process.env.NEXT_PUBLIC_NODE_DOMAIN;
if (!isPublicSwarmDomain(ourDomain)) {
return NextResponse.json(
{ error: 'This node is not configured for public swarm participation' },
{ status: 503 }
);
}
if (!isPublicSwarmDomain(data.sender)) {
return NextResponse.json(
{ error: 'Swarm nodes must use a public ICANN domain' },
{ status: 400 }
);
}
// Don't process gossip from ourselves
if (data.sender === ourDomain) {
if (getPublicSwarmDomain(data.sender) === getPublicSwarmDomain(ourDomain)) {
return NextResponse.json(
{ error: 'Cannot gossip with self' },
{ status: 400 }
+8
View File
@@ -19,6 +19,7 @@ import {
discoverNode,
} from '@/lib/swarm/discovery';
import { runGossipRound, gossipToNode } from '@/lib/swarm/gossip';
import { isPublicSwarmDomain } from '@/lib/swarm/node-domain';
/**
* GET /api/swarm/nodes
@@ -75,6 +76,13 @@ export async function POST(request: Request) {
const body = await request.json();
const { action, domain, priority } = actionSchema.parse(body);
if (domain && !isPublicSwarmDomain(domain)) {
return NextResponse.json(
{ error: 'Swarm nodes must use a public ICANN domain' },
{ status: 400 }
);
}
switch (action) {
case 'announce': {
if (domain) {