Skip to main content

Prerequisites

Use Node.js 20 or later. Set SILMARIL_API_KEY and SILMARIL_API_URL from your deployment access.

Install

Classify your first request

Create one client per protected system and reuse it. Label each call with the boundary it checks.
For tool output, pass the tool name as well.

Expected result

A benign request logs BENIGN, its score, and allow. Direct classify calls return a result in every mode. To enforce Block mode, stop the event when prediction is MALICIOUS or governance.action is block, then route it with firewall outcomes. See confidence scores for score meaning and threshold behavior. A per-call mode wins the client setting. Either local choice is sent with the request and preserved in result.mode. Without a local override, current classify responses omit mode, so result.mode remains absent. Direct calls are decision-only regardless: the caller chooses whether to enforce according to its local mode and application policy. A rejected promise means the call did not produce a verdict. That includes SilmarilApiError for a non-2xx API response, and a network, timeout, or abort error. With native fetch, a refused redirect raises a TypeError. Custom fetch implementations can differ. Decide whether that boundary fails open or closed. Vercel, LangChain, and LangGraph integrations that wrap this client are documented in Framework SDKs.

Concurrent requests

Reuse one client for concurrent calls in a JavaScript runtime when those calls are independent events or different conversations. Ordered events that share one metadata.conversationId wait for each classify() call before the next event is sent. Pass signal to classify or classifyBatch to cancel one request and its retry wait. Create a client in each worker thread.
request.signal is the caller’s AbortSignal. classifyBatch() cancels as one request. Its items stay independent of one another.

Batch calls and conversation history

classifyBatch() requires a predictions array with one result per input. Missing or mismatched results reject with an Error, including during retries. Pass metadata: { conversationId } on each ordered classify() call. Wait for and handle its verdict or error before sending the next event. Batch calls preserve per-item metadata and treat items independently of conversation history. See events and conversations for sequencing and modes and enforcement for returned verdicts in Warn and Shadow.

Classify in parallel

From SDK 0.10.0, protect() runs your call while its input is classified instead of waiting for the verdict first. It accepts the classify() options plus the input text, and resolves with your call’s result.
callModel, prompt, and conversationId belong to your application. Unlike direct classify(), protect() enforces the verdict. Without a local mode, protect() uses the Block fallback. The optional output function extracts text from the result. That text is classified as llm_output before the result is returned in Block mode, and in the background in Shadow or Warn. Use protect() for calls whose effect can be discarded, such as a model call. Classify the arguments of a tool that writes with classify() before running it. Classifications that share one metadata.conversationId are sent in the order they started, so protect() keeps the conversation sequence without waiting between events. Two client options bound this work: maxConcurrent (default 32) limits classifications in flight, and maxQueued (default 64) limits unjoined background classifications per conversation. A background classification retries once. Background classifications are also capped at maxConcurrent * maxQueued across all conversations. A failed one is logged with console.warn and never rejects, and one that would exceed either limit is dropped with a warning. Metadata and governance are copied when the call starts, so changing them afterwards does not change a classification already under way. Call await fw.flush() to wait for background classifications, for example before a serverless handler returns.

Shadow mode

An explicit local Shadow mode reports would-block decisions with the same thresholds while traffic continues. In Shadow mode, LangChain handlers and Deep Agents middleware classify in the background, so onClassify runs after the callback returns; set failOpen: false to keep those checks inline. Vercel middleware classifies inline in every mode. Framework adapters resolve an adapter override first, then the client setting, then an optional response mode, and finally a Block fallback. Current classify responses omit mode, so set Shadow or Warn locally when an adapter should observe rather than block.

Enforce one boundary

Attach enforcingHandler to the model or graph whose callbacks should enforce.

Errors

Blocking behavior depends on the integration. protect(), Vercel non-streaming generation, and LangChain callbacks throw FirewallBlockedException when they enforce a malicious or governance-block decision. The exception carries score and threshold.
generateText and model come from the Vercel integration. With Vercel streaming, an output block surfaces as an error stream part after any text already emitted. Deep Agents replaces denied tool interactions or model content with fixed safe messages in Block mode. See Deep Agents. Rethrow any non-block error from non-streaming calls according to your application error policy.

HTTP 408 handling

TypeScript retries HTTP 429 only. HTTP 408, including the direct runtime request-body timeout before classification, is surfaced as SilmarilApiError. This timeout closes the connection and produces no verdict. It does not establish replay safety for model or tool operations.

Review depth

See review depth for minimum SDK versions and selection behavior. The public ReviewLevel type is "standard" | "deep". Set review in Firewall options for an explicit client default, or in classify and classifyBatch options for a per-call selection. Resolution is explicit per-call value, then explicit client default, then omission of the wire field. The SDK never invents an implicit default.
Use the exact strings standard and deep. Invalid values, including null, fail SDK validation. Missing or mismatched backend acknowledgement produces ReviewCompatibilityError. See review depth for runtime requirements and response handling.

Next steps

Verify your integration, then review modes and enforcement.