Block secrets in a Fastify app
Register the OpenAI-compatible Fastify plugin so chat requests and SSE responses respect Tailrace policy before they reach the model.
Use @tailrace/fastify to enforce data policy on OpenAI-compatible chat routes - in-process, with no proxy.
Prerequisites
- Quickstart or equivalent
createTailracesetup - Fastify
>=4
Step 1: Install
pnpm add @tailrace/core @tailrace/fastify fastifyStep 2: Register the plugin
import { createTailrace } from "@tailrace/core";
import { tailraceFastify } from "@tailrace/fastify";
import Fastify from "fastify";
const app = Fastify();
const tailrace = createTailrace();
await app.register(
tailraceFastify(tailrace, {
agent: (req) => String(req.headers["x-agent-id"] ?? "default"),
workflowId: (req) => String(req.headers["x-workflow-id"] ?? "default"),
}),
);
// Your upstream proxy / chat handler routes...
await app.listen({ port: 3000 });The plugin uses preHandler for the request check and onSend / stream wrapping for JSON and SSE
responses. Ensure chat routes receive a parsed JSON body (Fastify's default JSON parser is enough).
How it works
| Location | Tailrace boundary | Direction |
|---|---|---|
| Chat request messages | { kind: "model", provider } (model field as-is) | Outbound to upstream |
| JSON chat completion | same model boundary | Inbound |
SSE text/event-stream | same; carry-buffer across chunks | Inbound |
When policy resolves to block, the plugin returns 422 with
{ error: { type: "policy_violation", entity, rule } } - never the raw value. That maps from
PolicyViolationError.
For SSE, Tailrace cancels the upstream stream, emits one error data: event, and closes
(abort-only in v0.1).
Only mode: "openai-compatible" is supported. Shared helpers live in @tailrace/http.
Common variations
Scope agent and workflow ID
Pass agent and workflowId as functions of FastifyRequest (or a static string for
workflowId). Defaults are "default" when omitted.
By default, workflowId scopes tokens per request. For multi-turn agents that need tokens to
resolve consistently across turns, see Tokenization and the vault.
NestJS + Fastify
If you use Nest's Fastify adapter, register @tailrace/fastify on the underlying Fastify instance
rather than @tailrace/nestjs Express-shaped middleware. See
Block secrets in a NestJS app.
Verify with a synthetic key
Send a chat completion body whose messages include sk_test_…FAKE. Expect 422. An
example.com email should tokenize under the default policy.
See also
- Fastify integration
- Fastify reference
- Hono - same openai-compat contract on a different host
- Boundaries
- Spec:
docs/integrations.md§11