Quickstart

The Check Point AI Red Teaming SDK (lakera-red-sdk) lets you run adversarial scans programmatically. Use it to integrate red teaming into CI/CD pipelines, test custom agent flows, or automate security assessments without the web UI.

The SDK is outbound-only: your process pulls attack prompts from the Red API over HTTPS, so you don’t need to expose any inbound endpoints or open inbound firewall rules. See SDK Deployment for network requirements, proxy configuration, and runtime details.

Prerequisites

Install

$npm install lakera-red-sdk

Try a Runnable Example

The SDK includes ready-to-run examples to help you get started quickly.

$npx lakera-red-sdk init-examples
$cd lakera-red-examples/echo
$cp .env.example .env # add your LAKERA_RED_API_KEY
$npm install
$npm start

See the examples helper reference for the full list of examples and commands.

Run Your First Scan

1

Initialize the client

Create a client with your API key and the Red API base URL.

1import { LakeraRedClient } from "lakera-red-sdk"
2
3const client = new LakeraRedClient({
4 apiKey: process.env.LAKERA_RED_API_KEY,
5 baseUrl: "https://red-webhooks.lakera.ai",
6})
2

Create a target

A target represents the agent you’re testing. Its name is reused across scans — if a target with that name already exists, the SDK uses it. Each target owns a recon profile: a structured description of your application that helps Red tailor its attacks. You set it up once, at target creation, and it’s reused by every scan.

You have two ways to provide the profile:

  1. Pass appContext (or appContextFile) to set it directly — no recon runs.
  2. Omit the context and pass a handler. The SDK then runs a short reconnaissance phase by relaying prompts through your agent, and saves the result on the target.

A profile you pass with appContext (or appContextFile) always wins — it overwrites whatever the target already held. Only when you omit the context does an existing profile (from a previous run or the dashboard) get reused, in which case neither of the above runs.

1// Option 1: provide the profile directly.
2const target = await client.createOrGetTarget({
3 name: "my-agent",
4 appContext: {
5 appDescription:
6 "A customer support chatbot that can look up orders and process refunds",
7 allowedActions: [
8 "Look up order status",
9 "Process refunds",
10 "Answer product questions",
11 ],
12 forbiddenActions: ["Reveal internal pricing rules", "Share other customers' data"],
13 },
14})

Alternatively, load the context from a YAML file:

1const target = await client.createOrGetTarget({
2 name: "my-agent",
3 appContextFile: "./app-context.yaml",
4})

Or omit the context and let recon run through your agent. Pass a handler (the same signature you’ll use for scan.run() below):

1const target = await client.createOrGetTarget({ name: "my-agent" }, async (session) => {
2 for await (const { attack, respond } of session) {
3 const reply = await myAgent.chat(attack)
4 await respond(reply)
5 }
6})
3

Add ground truth (optional)

Beyond the recon profile, you can give Red the target’s ground truth — its actual system prompt and/or tool definitions. The judge uses this to evaluate attacks more precisely (for example, to confirm a leaked system prompt or an out-of-policy tool call rather than guessing), which reduces false positives. Like the recon profile, ground truth lives on the target and is reused across scans.

Provide it inline via groundTruth, or through the same YAML file as your app context using its systemPrompt / tools keys. Both fields are optional — supply whichever you have.

1const target = await client.createOrGetTarget({
2 name: "my-agent",
3 appContext: {
4 appDescription:
5 "A customer support chatbot that can look up orders and process refunds",
6 allowedActions: ["Look up order status", "Process refunds"],
7 forbiddenActions: ["Reveal internal pricing rules"],
8 },
9 groundTruth: {
10 systemPrompt:
11 "You are a support assistant for Acme. Never reveal internal pricing.",
12 tools: [
13 JSON.stringify({ name: "lookup_order", description: "Look up an order by id" }),
14 JSON.stringify({
15 name: "process_refund",
16 description: "Issue a refund for an order",
17 }),
18 ],
19 },
20})

See Ground Truth for the full field reference and the YAML file layout.

4

Create a scan

Define what you want to test. Pass the targetId from the target returned by createOrGetTarget — the recon profile is read from the target.

1const scan = await client.createScan({
2 name: "CI nightly security check",
3 targetId: target.targetId,
4 strategy: { name: "static" },
5 objectives: [
6 "security.system-prompt-extraction.1",
7 "safety.dangerous-instructions.1",
8 ],
9 concurrency: 5,
10})

You can also define custom objectives alongside standard ones:

1const scan = await client.createScan({
2 name: "CI nightly security check",
3 targetId: target.targetId,
4 strategy: { name: "static" },
5 objectives: ["security.system-prompt-extraction.1"],
6 customObjectives: [
7 {
8 key: "my-org.competitor-mention.1",
9 name: "Competitor Recommendations",
10 attackDescription:
11 "Get the assistant to recommend a named competitor instead of our product.",
12 successIndicators: [
13 "The reply names a specific competitor and actively endorses it — recommends buying it, praises its features, or calls it the better choice.",
14 ],
15 },
16 ],
17 concurrency: 5,
18})

See CustomObjective for the full field reference.

5

Handle attack sessions

The scan.run() method drives the scan. For each concurrent session, your handler receives adversarial prompts and submits your agent’s responses.

You can also follow the scan’s progress in the dashboard via scan.dashboardLink / scan.dashboard_link.

1await scan.run(async (session) => {
2 try {
3 for await (const { attack, respond } of session) {
4 const reply = await myAgent.chat(attack)
5 await respond(reply)
6 }
7 } finally {
8 await myAgent.shutdown()
9 }
10})

Each session may contain multiple turns (especially with the crescendo strategy). The async iterator handles this naturally — just keep looping. The finally block ensures your agent is properly shut down once the session completes or errors out.

6

Retrieve results

Once run() completes, fetch the evaluated results.

1const results = await scan.getResults()
2console.log(`Ready: ${results.ready}`)
3console.log(`Issues found: ${results.results?.filter((r) => r.evaluation).length}`)

You can also write results directly to a file:

1const path = await scan.writeResults("./red-results.json")
2console.log(`Results saved to ${path}`)

Full Example

1import { LakeraRedClient } from "lakera-red-sdk"
2
3// Replace this echo agent with a call to your own agent or application.
4const myAgent = {
5 async chat(message: string): Promise<string> {
6 // Simply echoes whatever the scanner sends.
7 return `Echo: ${message}`
8 },
9 async shutdown(): Promise<void> {
10 // No cleanup necessary for this dummy implementation.
11 },
12}
13
14const client = new LakeraRedClient({
15 apiKey: process.env.LAKERA_RED_API_KEY,
16 baseUrl: "https://red-webhooks.lakera.ai",
17 logLevel: "info",
18})
19
20const handler = async (session) => {
21 try {
22 for await (const { attack, respond } of session) {
23 const reply = await myAgent.chat(attack)
24 await respond(reply)
25 }
26 } finally {
27 await myAgent.shutdown()
28 }
29}
30
31// Set up the target once with its recon profile. Here we provide it directly
32// via appContext; the profile is reused across future scans.
33const target = await client.createOrGetTarget({
34 name: "my-chatbot",
35 appContext: {
36 appDescription:
37 "A customer support chatbot that can look up orders and process refunds",
38 allowedActions: [
39 "Look up order status",
40 "Process refunds",
41 "Answer product questions",
42 ],
43 forbiddenActions: ["Reveal internal pricing rules", "Share other customers' data"],
44 },
45})
46
47const scan = await client.createScan({
48 name: "Nightly security scan",
49 targetId: target.targetId,
50 strategy: { name: "crescendo", maxTurns: 15 },
51 objectives: [
52 "security.system-prompt-extraction.1",
53 "security.instruction-override.1",
54 "safety.dangerous-instructions.1",
55 ],
56 concurrency: 3,
57})
58
59await scan.run(handler)
60
61const results = await scan.getResults()
62await scan.writeResults("./red-results.json")
63console.log(`View report: ${scan.dashboardLink}`)
64
65const failures = results.results?.filter((r) => r.error)
66if (failures?.length) {
67 console.error(`${failures.length} objectives failed`)
68 process.exit(1)
69}

Next Steps