---
title: "Switch things off in an incident without a deploy"
description: "Kill an action, route, driver or agent, make the app read-only or sign people out with one command, see what is in force and let it expire."
section: "guides"
status: "Building for 0.4"
spec: "incident-controls"
canonical: "https://mainsdk.dev/docs/guides/switch-things-off-in-an-incident"
updated: "2026-10-28"
---

# Switch things off in an incident without a deploy

> Kill an action, route, driver or agent, make the app read-only or sign people out with one command, see what is in force and let it expire.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/switch-things-off-in-an-incident · Updated: 2026-10-28

You will kill one route, set the app read-only, read what is in force and clear both. It takes about 15 minutes. Every control is a pushed value that only tightens what `main.yaml` allows, so it takes effect on every instance in seconds and needs no deploy.

## You need

- A MainSDK app with a `config` driver that can write: `files` in development, or your store in production.
- For an admin screen, `auth` and a role that holds `incident.manage`.

## 1. Kill a route

```bash
npx mainsdk incident on kill route:/api/export/* --reason "leak, #812" --expires 4h
```

A target is `action:<name>`, `route:<path>`, `interface:<name>`, `driver:<id>`, `agent:<name>` or `tool:<name>`. A reason is required. The killed route answers 503 `MAIN-INCIDENT-0001` before its handler runs, and the problem names the target, not the reason.

## 2. Go read-only

```bash
npx mainsdk incident on read-only --reason "leak, #812"
```

Unsafe requests (`POST`, `PUT`, `PATCH`, `DELETE`), database writes, uploads and event publishes are refused with `MAIN-INCIDENT-0002`; reads keep working. Paths listed under `incident.read-only-allow` in `main.yaml`, such as `/api/webhooks/*`, still take writes. To sign people out instead, use `incident on sign-out [--tenant tnt_…]`; API keys keep working.

## 3. Read what is in force

```bash
npx mainsdk incident status
```

Every response carries a `MainSDK-Incident` header naming the controls in force, for example `kill, read-only`.

## 4. Clear it

```bash
npx mainsdk incident off kill route:/api/export/* --reason "fixed in 1.4.2"
npx mainsdk incident off read-only --reason "fixed in 1.4.2"
```

A control with `--expires` ends on its own. One without it makes `check` and the log warn after 24 hours (`MAIN-INCIDENT-0007`).

## 5. Let an admin do it from a screen

```ts title="app/api/admin/read-only/route.ts"
import { incident } from "mainsdk";

export async function POST(req: Request) {
  await incident.set(req, { readOnly: true }, { reason: "leak, #812" });
  return Response.json(await incident.status());
}
```

The caller needs `incident.manage` and a phishing-resistant session less than five minutes old, else `MAIN-INCIDENT-0006` (403) and nothing is written.

## Verify

```bash
npx mainsdk check
```

Exit 0 means every `incident` entry in `main.yaml` names a known target and every path in `read-only-allow` is a path.

## If it fails

| Code | What it means | Fix |
| --- | --- | --- |
| `MAIN-INCIDENT-0004` | A kill names an action, agent or interface the app does not have | Fix the target |
| `MAIN-INCIDENT-0005` | No reason, or the config driver cannot write | Pass `--reason`; use a driver that writes, or set the printed key by hand |
| `MAIN-INCIDENT-0006` | `incident.set` without `incident.manage` or a fresh session | Sign in again with a passkey; grant the permission |
| `MAIN-INCIDENT-0007` | A control has been in force over 24 hours with no expiry | Clear it, or set `--expires` |

## For agents

```text
Goal: stop the export route for four hours, then clear it.
Touch: nothing in code; run the mainsdk incident commands.
Do not: write your own kill flag or maintenance mode; loosen main.yaml; omit --reason.
Verify: npx mainsdk incident status shows the control; a request to the route gets 503.
Stop and ask if: the target is unknown or the person has not said which route to stop.
```

## Next

- [Tell the right person once with alerts](https://mainsdk.dev/docs/guides/raise-and-route-alerts)
- [Respond to an attack or CPU pressure](https://mainsdk.dev/docs/guides/respond-to-attacks-and-pressure)
- [Change config without a deploy](https://mainsdk.dev/docs/guides/change-config-without-a-deploy)
