← Developers
Level 1: code Framework
TypeScript: LangChain.js and LangGraph.js
Node resolves through the system resolver by default, so Level 0 applies without changes. This page adds the guard and a tool that uses it.
Configuration and reference code for TypeScript: LangChain.js and LangGraph.js
The guard
The guard compares the answer against the block page address.
securdGuard.ts
// securdGuard.ts
// Held and blocked names resolve to the policy's block page address.
// Put that address (from the console, Sites > your site) in SECURD_BLOCK_ADDRS.
import { promises as dns } from "node:dns";
const BLOCK_ADDRS = new Set((process.env.SECURD_BLOCK_ADDRS ?? "").split(",").filter(Boolean));
export class SecurdHeld extends Error {
constructor(public host: string, public agentRole: string) {
super(`${host} is held or blocked for ${agentRole}`);
}
}
export async function securdGuard(host: string, agentRole: string): Promise<void> {
let answers: string[];
try {
answers = (await dns.lookup(host, { all: true })).map((a) => a.address);
} catch {
console.warn(JSON.stringify({ event: "securd.unresolved", agentRole, host }));
throw new SecurdHeld(host, agentRole);
}
if (answers.some((a) => BLOCK_ADDRS.has(a))) {
console.warn(JSON.stringify({ event: "securd.held", agentRole, host }));
throw new SecurdHeld(host, agentRole);
}
}
A guarded tool
tools.ts
import { DynamicTool } from "@langchain/core/tools";
import { securdGuard, SecurdHeld } from "./securdGuard";
const AGENT_ROLE = "docs-agent";
export const fetchPage = new DynamicTool({
name: "fetch_page",
description: "Fetch a page this agent is approved to read.",
func: async (url: string) => {
const host = new URL(url).hostname;
try {
await securdGuard(host, AGENT_ROLE);
} catch (e) {
if (e instanceof SecurdHeld) return `BLOCKED: ${e.message}`;
throw e;
}
const res = await fetch(url);
return (await res.text()).slice(0, 8000);
},
});
Evaluate Agent DNS with your own agent traffic
Deploy on a single policy in learning mode and review the recorded destinations with your team.