MainSDK
Search⌘K
How-to

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.

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 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

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

CodeWhat it meansFix
MAIN-INCIDENT-0004A kill names an action, agent or interface the app does not haveFix the target
MAIN-INCIDENT-0005No reason, or the config driver cannot writePass --reason; use a driver that writes, or set the printed key by hand
MAIN-INCIDENT-0006incident.set without incident.manage or a fresh sessionSign in again with a passkey; grant the permission
MAIN-INCIDENT-0007A control has been in force over 24 hours with no expiryClear 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

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet