As a compliance officer at a vertical SaaS provider, you must ensure that AI features adhere to industry-specific regulations like HIPAA for vet-tech or PCI for restaurant-tech. Off-the-shelf guardrails are too generic and don't map to your vertical workflows. You need a way to define and enforce safety policies per tenant, with observability into violations. Your current approach relies on manual reviews, which doesn't scale.
A complete, working implementation of this recipe — downloadable as a zip or browsable file by file. Generated by our build pipeline; tested with full coverage before publishing.
In this tutorial you’ll build a vertical-specific safety guardrail system that enforces HIPAA, PCI, and SOC2 compliance rules per tenant. You’ll wire six REAA packages into a Next.js + Hono API application, compose per-vertical guardrail chains with PII redaction, prompt injection detection, and topic boundaries, and add a tool-use firewall that blocks unauthorized tool calls. By the end you’ll have a working API server with 69 integration tests you can run with a single command.
Prerequisites
Node.js 22+ and pnpm 10+
A terminal (Linux, macOS, or WSL)
Familiarity with TypeScript, Next.js App Router, and REST API patterns
No API keys needed — all REAA packages run locally
Step 1: Scaffold the project and install dependencies
Create the Next.js project with the App Router, then install the six REAA packages plus Hono and the YAML parser.
Expected output:dependencies in your package.json now lists all packages with exact version pins (no ^ or ~). Your dependencies block should look like this:
Each compliance vertical gets its own YAML config file. Create the config/ directory and three files that define which guardrails run for each vertical.
Expected output: Running ls config/ shows three .guardrail.yaml files. Each defines a budget block (maxLatencyMs: 500, maxTokens: 4000) and a set of guardrails with type (input/output), priority, and essential flags.
Step 3: Define the shared types
Create the src/ directory, then create src/types.ts — this single file holds every interface and type the services share. It imports the LoadedConfig type from @reaatech/guardrail-chain-config to support per-tenant guardrail overrides.
Expected output:pnpm typecheck passes. The types file defines the VerticalType union, request/response body interfaces for both the guardrail evaluation and tool-call protection endpoints, plus the TenantConfig and FirewallRule interfaces that connect tenants to their compliance vertical.
Step 4: Set up observability
Create src/services/observability.ts to wire logging, metrics, and tracing from @reaatech/guardrail-chain-observability. The metrics adapter records counts in a plain Map<string, number> that you can inspect later.
Expected output:initObservability() wires a ConsoleLogger, an in-memory metrics collector, and a no-op tracer into the REAA observability globals. You can later call getLogMetrics() to read the counters.
Step 5: Create the tenant store
The InMemoryTenantStore holds tenant configurations in a Map. It’s seeded with three default tenants — one per compliance vertical — so you can evaluate guardrails without creating tenants first.
Expected output: The tenant store is a singleton. When the module loads, it automatically seeds three tenants: tenant-hipaa, tenant-pci, and tenant-soc2.
Step 6: Build the config loader
The config loader uses @reaatech/guardrail-chain-config to load vertical YAML files and merge per-tenant overrides.
Create src/services/config-loader.ts:
ts
import { loadConfig, validateConfigSafe } from "@reaatech/guardrail-chain-config";import type { LoadedConfig } from "@reaatech/guardrail-chain-config";import type { VerticalConfigMap, VerticalType, TenantConfig } from "../types.js";export async function loadVerticalConfig(vertical: VerticalType): Promise<LoadedConfig> { return loadConfig({ filePath: `config/${vertical}.guardrail.yaml` });}export async function loadAllVerticalConfigs(): Promise<VerticalConfigMap> { const [hipaa, pci, soc2] = await Promise.all([ loadVerticalConfig("hipaa"), loadVerticalConfig("pci"), loadVerticalConfig("soc2"), ]); return { hipaa, pci, soc2 };}export async function getConfigForTenant(tenant: TenantConfig): Promise<LoadedConfig> { const baseConfig = await loadVerticalConfig(tenant.vertical); if (!tenant.guardrailOverrides) return baseConfig; const result = validateConfigSafe({ ...baseConfig, ...tenant.guardrailOverrides }); if (result.success && result.config) return result.config; return baseConfig;}
Expected output:loadVerticalConfig("hipaa") reads config/hipaa.guardrail.yaml and returns a parsed LoadedConfig. loadAllVerticalConfigs() loads all three in parallel via Promise.all. getConfigForTenant() merges any per-tenant guardrailOverrides on top of the base vertical config.
Step 7: Compose the guardrail chain service
This is the heart of the recipe. The guardrail service builds a ChainBuilder pipeline for each vertical, registering the specific guardrails that compliance vertical needs. HIPAA gets PII redaction with masking and topic blocking for PHI terms. PCI gets PII redaction with removal and cost prechecks. SOC2 gets content moderation and sentiment analysis.
Create src/services/guardrail-service.ts:
ts
import { ChainBuilder, getLogger, GuardrailError } from "@reaatech/guardrail-chain";import type { ChainResult } from "@reaatech/guardrail-chain";import { PIIRedaction, PromptInjection, TopicBoundary, CostPrecheck, ContentModeration, MemoryLimit, PIIScan, HallucinationCheck, ToxicityFilter, SentimentAnalysis,} from "@reaatech/guardrail-chain-guardrails";import type { TenantConfig, VerticalType, EvaluateResponseBody } from "../types.js";export function buildGuardrailChain(vertical: VerticalType) { const builder = new ChainBuilder() .
Expected output: Each vertical builds a chain with different guardrails. HIPAA uses PIIRedaction with redactionStrategy: "mask" and a TopicBoundary blocking phi, medical-record, ssn, and patient-data. PCI uses redactionStrategy: "remove" and a CostPrecheck. SOC2 uses ContentModeration, MemoryLimit, SentimentAnalysis, and HallucinationCheck instead of PII-focused guardrails.
Step 8: Build the tool-use firewall service
The firewall service checks tool calls against tenant-level policies using @reaatech/tool-use-firewall-policies. It runs the PolicyEngine first, then the SecretScanner, and returns block/continue decisions.
Expected output: When a tool call arrives, protectToolCall() builds a RequestContext with a random request ID, runs the policy engine evaluation, then runs the secret scanner. If either returns BLOCK, the tool call is denied. If both pass, the call is allowed to continue.
Step 9: Wire everything together with Hono
The Hono app creates all API routes and serves them through the hono/vercel adapter. This is where the tenant store, guardrail service, and firewall service converge into HTTP endpoints.
terminal
mkdir -p src/lib
Create src/lib/hono-app.ts:
ts
import { Hono } from "hono";import { handle } from "hono/vercel";import { evaluateInput, evaluateBoth } from "../services/guardrail-service.js";import { protectToolCall } from "../services/firewall-service.js";import { tenantStore } from "../services/tenant-store.js";import type { EvaluateRequestBody, EvaluateResponseBody, ProtectRequestBody, TenantConfig } from "../types.js";function createGuardrailApp() { const app = new Hono(); app.post("/api/guardrails/evaluate", async (c) => { const body = await c.req.
Expected output: The Hono app exposes eight endpoints: health check, guardrail evaluation (with optional output), tool-call protection, and full CRUD for tenants. Unknown tenants default to SOC2 guardrails rather than returning a 404. When output is provided alongside input, both are evaluated and the results are merged into a single response that passes only when both pass.
Step 10: Connect Next.js and enable instrumentation
Create the App Router catch-all route that delegates to Hono, plus the instrumentation hook that initializes observability on server start.
terminal
mkdir -p app/api
Create app/api/[[...route]]/route.ts:
ts
import { handle } from "hono/vercel";import { app } from "../../../src/lib/hono-app.js";export const GET = handle(app);export const POST = handle(app);export const PUT = handle(app);export const DELETE = handle(app);
Create src/instrumentation.ts to initialize observability when Node.js starts:
ts
export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { const { initObservability } = await import("./services/observability.js"); initObservability(); }}
Update next.config.ts to enable the instrumentation hook — without this flag the register() function is dead code:
Expected output: The next.config.ts file has experimental: { instrumentationHook: true }. The catch-all route at app/api/[[...route]]/route.ts re-exports the Hono handler for all four HTTP methods. When the dev server starts, src/instrumentation.ts runs and calls initObservability() to wire up logging and metrics.
Step 11: Replace the placeholder index and page
Replace src/index.ts with a barrel export that re-exports every module and service so the full API surface is importable from one place:
ts
export * from "./services/guardrail-service.js";export * from "./services/firewall-service.js";export * from "./services/observability.js";export * from "./services/tenant-store.js";export * from "./services/config-loader.js";export * from "./types.js";export * from "./lib/hono-app.js";export const RECIPE_SLUG: string = "agnostic-vertical-safety-guardrails-2";
Replace app/page.tsx with a static status page that documents the available endpoints:
tsx
export default function Home() { return ( <main style={{ maxWidth: 720, margin: "0 auto", padding: "2rem 1rem", fontFamily: "system-ui, sans-serif" }}> <h1>Vertical-specific Safety Guardrails</h1> <p>Enforce HIPAA, PCI, or other compliance rules per tenant with customizable guardrails.</p> <h2>API Endpoints</h2> <ul> <li><code>GET /api/health</code> — Health check</li> <li><code>POST /api/guardrails/evaluate</code> — Evaluate input/output against guardrails</li> <li><code>POST /api/middleware/protect</code> — Protect tool calls via firewall</li> <li><code>GET /api/tenants</code> — List tenants</li> <li><code>POST /api/tenants</code> — Create tenant</li> <li><code>GET /api/tenants/:id</code> — Get tenant</li> <li><code>PUT /api/tenants/:id</code> — Update tenant</li> <li><code>DELETE /api/tenants/:id</code> — Delete tenant</li> </ul> </main> );}
Expected output: The home page renders a clean API documentation page. src/index.ts re-exports everything so other modules can import from ../../src/index.js.
Step 12: Run the tests
The test suite covers every service and API route with 69 tests across 11 files. The tests use vi.mock to simulate the REAA package internals. Here’s the tenant store test as an example:
Expected output: All 69 tests pass across 11 test files. The coverage report shows aggregate source coverage at 96% lines, 96% statements, 97% functions, and 93% branches (UI files like app/page.tsx are excluded from coverage). You should see numFailedTests=0 in the vitest report JSON.
Next steps
Add a database-backed tenant store — Replace InMemoryTenantStore with a PostgreSQL or SQLite adapter so tenant configurations persist across server restarts.
Add extra compliance verticals — Create new config/*.guardrail.yaml files for FedRAMP, GDPR, or SOX and register them in the VerticalType union.
Add a dashboard — Build a Next.js admin page that displays real-time guardrail violations per tenant using the metrics collected by getLogMetrics().