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 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.
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
configdriver that can write:filesin development, or your store in production. - For an admin screen,
authand a role that holdsincident.manage.
1. Kill a route
npx mainsdk incident on kill route:/api/export/* --reason "leak, #812" --expires 4hA 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
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
npx mainsdk incident statusEvery response carries a MainSDK-Incident header naming the controls in force, for example kill, read-only.
4. Clear it
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
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
npx mainsdk checkExit 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
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?