Build With The Firewall
Silmaril is an AI application firewall that protects agent execution.
Silmaril evaluates intent, application context, tool calls, and accumulated execution state together before harmful outcomes materialize. Personalized security comes from the firewall lifecycle. Traces, data flows, and threat-hunting discoveries feed defenses tuned to the system they protect.
We recommend one Silmaril firewall instance per system. Send classification requests from the layer that controls model execution. Use an SDK, a framework handler, a LiteLLM guardrail, or an AI gateway.
Silmaril Firewall is not self-serve yet. Book a call with the Silmaril team to provision credentials, choose a deployment path, and receive the endpoint used by these examples.
Quick Start
Pick the runtime that owns the inference call. Each panel shows the direct SDK path first, then the framework and gateway integrations that apply to that language.
Use the TypeScript SDK from services that own inference, tool execution, or orchestration boundaries.
Install the core SDK for direct classify calls.
npm install @silmaril-security/sdkCreate one firewall client with the API key and classify endpoint issued by Silmaril.
import { Firewall } from "@silmaril-security/sdk";
const fw = new Firewall({
apiKey: process.env.SILMARIL_API_KEY!,
apiUrl: process.env.SILMARIL_API_URL!,
});Send the hook and tool name with each call so the firewall sees the execution state in context.
import { HookLabel } from "@silmaril-security/sdk";
const result = await fw.classify(userInput, {
hook: HookLabel.USER_INPUT,
});
await fw.classify(toolOutput, {
hook: HookLabel.TOOL_RESPONSE,
toolName: "read_file",
});Add the AI SDK packages only when Vercel owns model execution. Gateway routing and middleware can share the same firewall client.
npm install ai@^5
npm install @ai-sdk/openaiimport { wrapLanguageModel, generateText } from "ai";
import { openai } from "@ai-sdk/openai";
const model = wrapLanguageModel({
model: openai("gpt-5.4"),
middleware: fw.asMiddleware({ scanOutput: true }),
});
await generateText({ model, prompt: userInput });import { gateway, generateText, wrapLanguageModel } from "ai";
const model = wrapLanguageModel({
model: gateway("anthropic/claude-sonnet-4.6"),
middleware: fw.asMiddleware({ scanOutput: true }),
});
await generateText({
model,
prompt: userInput,
providerOptions: {
gateway: { tags: ["surface:assistant"] },
},
});LangGraph.js can use the same LangChain callback handler through graph invocation config.
npm install @langchain/core @langchain/openaiimport { ChatOpenAI } from "@langchain/openai";
const handler = await fw.asLangChainHandler();
const model = new ChatOpenAI({ callbacks: [handler] });
await model.invoke("Hello");const handler = await fw.asLangChainHandler();
await graph.invoke(state, {
callbacks: [handler],
});Use shadow mode to measure would-block decisions with the same thresholds while traffic continues. Override individual adapters when a surface is ready to enforce. Direct classify calls return verdicts either way.
const fw = new Firewall({
apiKey: process.env.SILMARIL_API_KEY!,
apiUrl: process.env.SILMARIL_API_URL!,
shadowMode: true,
});
const handler = await fw.asLangChainHandler({
onClassify: (event) => {
if (event.blocked && event.shadowMode) {
telemetry.track("firewall.would_block", {
hook: event.hook,
score: event.result.score,
});
}
},
});
const enforcingHandler = await fw.asLangChainHandler({
shadowMode: false,
});Framework adapters throw typed block exceptions in enforcement mode.
import { PromptBlockedException } from "@silmaril-security/sdk";
try {
await generateText({ model, prompt: userInput });
} catch (err) {
if (err instanceof PromptBlockedException) {
audit({ score: err.score, threshold: err.threshold });
return;
}
throw err;
}Use the Python SDK from services that own inference, tool execution, or orchestration boundaries.
Install the core SDK for direct classify calls.
pip install silmaril-security-sdkCreate one firewall client with the API key and classify endpoint issued by Silmaril.
import os
from silmaril_security.sdk import Firewall
fw = Firewall(
api_key=os.environ["SILMARIL_API_KEY"],
api_url=os.environ["SILMARIL_API_URL"],
)Classify user input, tool output, model output, or system prompt content with explicit hook labels.
from silmaril_security.sdk import HookLabel
result = fw.classify(
user_input,
hook=HookLabel.USER_INPUT,
)
fw.classify(
tool_output,
hook=HookLabel.TOOL_RESPONSE,
tool_name="read_file",
)LangGraph can pass the same callback handler through runnable config.
pip install "silmaril-security-sdk[langchain]" langchain-openaifrom langchain_openai import ChatOpenAI
handler = fw.as_langchain_handler()
model = ChatOpenAI(callbacks=[handler])
model.invoke("Hello")handler = fw.as_langchain_handler()
graph.invoke(
state,
config={"callbacks": [handler]},
)Use shadow mode to return classification results while suppressing block exceptions. Per-call overrides let one boundary enforce before changing the client default.
import logging
import os
from silmaril_security.sdk import ClassifyEvent, Firewall, HookLabel
def on_classify(event: ClassifyEvent) -> None:
if event.blocked and event.shadow_mode:
logging.info("would block %s score=%.4f", event.hook, event.result.score)
fw = Firewall(
api_key=os.environ["SILMARIL_API_KEY"],
api_url=os.environ["SILMARIL_API_URL"],
shadow_mode=True,
on_classify=on_classify,
)
fw.classify(text, hook=HookLabel.TOOL_RESPONSE, shadow_mode=False)Direct Python calls raise typed block exceptions in enforcement mode.
from silmaril_security.sdk import PromptBlockedException
try:
fw.classify(user_input, hook=HookLabel.USER_INPUT)
except PromptBlockedException as exc:
audit({"score": exc.score, "threshold": exc.threshold})Use the Go SDK from services that own inference, tool execution, or orchestration boundaries.
Add the firewall module and import the firewall package from application code.
go get github.com/Silmaril-Security/sdk-go/firewall@latestCreate one firewall client with the API key and classify endpoint issued by Silmaril.
fw, err := firewall.New(firewall.Options{
APIKey: os.Getenv("SILMARIL_API_KEY"),
APIURL: os.Getenv("SILMARIL_API_URL"),
})
if err != nil { log.Fatal(err) }Pass hook metadata with each call so server decisions line up with where the text came from.
result, err := fw.Classify(ctx, userInput,
firewall.WithHook(firewall.HookUserInput),
)
_, err = fw.Classify(ctx, toolOutput,
firewall.WithHook(firewall.HookToolResponse),
firewall.WithToolName("read_file"),
)
_ = resultUse shadow mode to keep verdict telemetry flowing while typed blocking errors are suppressed. Per-call options can enforce one boundary before changing the client default.
fw, err := firewall.New(firewall.Options{
APIKey: os.Getenv("SILMARIL_API_KEY"),
APIURL: os.Getenv("SILMARIL_API_URL"),
ShadowMode: true,
OnClassify: func(event firewall.ClassifyEvent) {
if event.Blocked && event.ShadowMode {
log.Printf("would block %s score=%.4f", event.Hook, event.Result.Score)
}
},
})
_, err = fw.Classify(ctx, text,
firewall.WithHook(firewall.HookToolResponse),
firewall.WithShadowMode(false),
)Enforcement returns typed blocking errors so live code can branch cleanly on block decisions.
var blocked *firewall.PromptBlockedError
if errors.As(err, &blocked) {
log.Printf("blocked score=%.4f threshold=%.4f", blocked.Score, blocked.Threshold)
return
}Use the Java SDK from services that own inference, tool execution, or orchestration boundaries.
Add the SDK dependency to the service that calls the firewall.
dependencies {
implementation("com.silmaril.security:silmaril-security-sdk:0.3.1")
}Create one firewall client with the API key and classify endpoint issued by Silmaril.
import dev.silmaril.security.Firewall;
Firewall fw = Firewall.builder()
.apiKey(System.getenv("SILMARIL_API_KEY"))
.apiUrl(System.getenv("SILMARIL_API_URL"))
.build();Keep hook metadata close to the boundary being evaluated.
import dev.silmaril.security.HookLabel;
import dev.silmaril.security.ClassifyOptions;
BlockResult result = fw.classify(
userInput,
ClassifyOptions.builder()
.hook(HookLabel.USER_INPUT)
.build()
);Treat block decisions as typed exceptions so application control flow stays explicit.
try {
fw.classify(userInput, options);
} catch (PromptBlockedException blocked) {
audit(blocked.score(), blocked.threshold());
return;
}LiteLLM Guardrails
If LiteLLM already routes model traffic, add Silmaril as a guardrail on the proxy. Pre-call and post-call checks cover requests, responses, messages, and tool activity without changing application code.
Use warn mode while validating proxy coverage. Silmaril still evaluates the payload and returns warning metadata for would-block traffic, but LiteLLM keeps the call moving. Switch to enforcement when the gateway path is ready to stop unsafe traffic.
guardrails:
- guardrail_name: silmaril-firewall
litellm_params:
guardrail: generic_guardrail_api
mode: [pre_call, post_call]
api_base: os.environ/SILMARIL_GUARDRAIL_URL
headers:
x-api-key: os.environ/SILMARIL_API_KEY
default_on: true
unreachable_fallback: fail_open
additional_provider_specific_params:
on_error: warnContainer Deployments
For customer-controlled deployments, run the firewall service and model as containers in the cloud account that holds the protected workload. SDKs use the deployed classify endpoint. LiteLLM uses the guardrail endpoint from the same stage.