---
title: "Respond to an attack or CPU pressure"
description: "Turn on signals and the attack, pressure and key-rotation playbooks, see what they would do, approve a run and watch it roll back."
section: "guides"
status: "Building for 0.4"
spec: "playbooks"
canonical: "https://mainsdk.dev/docs/guides/respond-to-attacks-and-pressure"
updated: "2026-10-28"
---

# Respond to an attack or CPU pressure

> Turn on signals and the attack, pressure and key-rotation playbooks, see what they would do, approve a run and watch it roll back.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/respond-to-attacks-and-pressure · Updated: 2026-10-28

You will turn on the three go-live playbooks, ask what each would do, then run one in a test with a fake clock. It takes about 20 minutes.

## You need

- A MainSDK app with a `main.yaml` that has a `limits` policy (see [rate limits](https://mainsdk.dev/docs/interfaces/limits)).

## 1. Turn them on

```yaml title="main.yaml"
signals: {}
playbooks:
  use: [attack, pressure]
  presets:
    attack: { limits: [sign-in], posture: high }
    pressure: { flags: [recommendations] }
```

Run `npx mainsdk check`. A step whose interface is not built yet is reported as `MAIN-PLAYBOOK-0002`; the rest of the rule still runs.

## 2. Ask what a rule would do

```bash
npx mainsdk playbooks list
npx mainsdk playbooks plan pressure.slim
```

Nothing is applied. The plan lists each step as `planned` or `skipped`, with one sentence of why.

## 3. Play it in a test

```ts title="test/respond.test.ts"
import { createClock, createTestApp } from "@mainsdk/testing";

const clock = createClock();
const app = createTestApp({ clock, yaml: `schema: 1\nproject: prj_test\nlimits:\n  policies:\n    sign-in: { max: 100, per: 1m }\nplaybooks:\n  use: [attack]\n  presets:\n    attack: { limits: [sign-in], posture: high }\n` });
const run = await app.playbooks.run("attack.contain", { reason: "drill" });
// attack.contain asks first: run.state is "waiting"
app.playbooks.approve(run.id, { kind: "user", id: "me" });
await new Promise((r) => setTimeout(r, 50));         // the overlay applies off the call
console.log(app.live.get("main.limits.sign-in"));   // 20/min
clock.advance(31 * 60_000);                          // after 30 minutes it expires on its own
await new Promise((r) => setTimeout(r, 50));
console.log(app.live.get("main.limits.sign-in"));   // 100/min
```

## 4. Approve in dev, and undo

While `mainsdk dev` runs, a waiting run is approved or declined from the command line, and any run can be undone with a reason:

```bash
npx mainsdk playbooks runs --state waiting
npx mainsdk playbooks approve <run>
npx mainsdk playbooks revert <run> --reason "false alarm"
```

## What to do next

- Write a rule of your own: [playbooks](https://mainsdk.dev/docs/interfaces/playbooks).
- Emit your own signal for a rule to match: [signals](https://mainsdk.dev/docs/interfaces/signals).
- Set the guard's ceilings with `guard:` in `main.yaml`.
