MainSDK
Search⌘K
How-to

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.

Building for 0.4

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
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).

1. Turn them on

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

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.
  • Emit your own signal for a rule to match: signals.
  • Set the guard's ceilings with guard: in main.yaml.

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet