Security
Security model and best practices for Fabrik UI
Never put API keys in frontend code
This is the #1 security mistake with LLM apps. Fabrik enforces a server/client split — API keys stay on the server.
How it works
Client sends a message
The browser sends user text to your Next.js API route via POST /api/chat. No API key is included.
Server calls the LLM
Your API route uses handler() which reads the API key from environment variables and streams the response from OpenAI/Anthropic/Google.
Server streams back events
The response is an SSE stream of StreamEvent objects — text deltas, component props, elicitation configs. No raw LLM internals are exposed.
Client renders the UI
The server() adapter on the client reads the SSE stream and renders messages, components, and artifacts.
What the client sees
The SSE stream contains only:
- Text deltas — incremental text chunks
- Component props — validated by your Zod schema
- Elicitation configs — structured question definitions
- Artifact content — code blocks and HTML
- Lifecycle events — start, done, error
The client never sees raw tool call arguments, system prompts, or internal reasoning.
Schema validation
All component props are validated server-side with Zod:
// 1. LLM outputs JSON: { "city": "SF", "temp": 72 }
// 2. Fabrik validates against your schema
const schema = z.object({ city: z.string(), temp: z.number() })
// 3. Only validated props are sent to the client
// 4. If validation fails → error event, not a renderEnvironment variables
Store API keys in .env.local (never committed to git):
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_GENERATIVE_AI_API_KEY=AI...The provider adapters read these automatically:
| Provider | Environment variable |
|---|---|
| AI SDK (recommended) | |
@ai-sdk/openai | OPENAI_API_KEY |
@ai-sdk/anthropic | ANTHROPIC_API_KEY |
@ai-sdk/google | GOOGLE_GENERATIVE_AI_API_KEY |
| Native adapters | |
@fabrik-sdk/ui/openai | OPENAI_API_KEY |
@fabrik-sdk/ui/anthropic | ANTHROPIC_API_KEY |
@fabrik-sdk/ui/google | GOOGLE_AI_API_KEY |
Artifact sandboxing
HTML artifacts render inside iframes with sandbox="" — the most restrictive mode:
- No JavaScript execution
- No form submissions
- No navigation away from the page
- No access to parent window
referrerPolicy="no-referrer"prevents data leaking via headers
Code artifacts are sanitized with defense-in-depth HTML stripping before rendering.
Best practices
Security checklist
- Use
handler()+server()pattern — never pass API keys to the client - Define strict Zod schemas — limits what the LLM can render
- Add
.env.localto.gitignore - Set
maxStepsto prevent runaway tool call chains - Use
beforeSendto filter user input
<Fabrik
provider={provider}
maxSteps={5}
beforeSend={(text) => {
if (text.length > 2000) return null // reject oversized input
return text
}}
>
<Chat />
</Fabrik>