2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:53:33 -04:00
2026-09-28 14:52:13 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00
2026-09-28 14:49:01 -04:00

KillSwitch

Checks that a terminated deployment is actually gone, resource class by resource class, including the billing that tends to outlive it.

What it does and why

Deleting is straightforward. Proving that nothing remains is harder, and the two failures that matter are both invisible to the delete path itself.

Billing outliving compute

The servers stop, the DNS record goes, and the subscription keeps charging. Each half looks correct on its own: the teardown succeeded, and the subscription is in a normal active state. Only the combination is wrong, and the customer is the one who finds out. No per-resource check can see this, so KillSwitch reports it as a separate critical finding and prints it first.

A delete response taken as a verification

A DELETE that returns 200 means the API accepted the request. It does not mean the resource is gone. A teardown whose only evidence is its own success response has verified nothing, so an absent state backed only by delete-response is reported as not assessed.

KillSwitch never deletes anything. It takes an inventory you enumerated and decides whether the removal can be evidenced. There is no network access and no cloud SDK.

state and verifiedBy Result
absent, independent-probe Met. The only real pass.
absent, delete-response Not assessed. The API accepted a request.
absent, assumed or unset Not assessed. An assumption is not a verification.
unknown Not assessed. Nobody looked.
present with retainedBy Not applicable, and still printed.
present Not met. This is residue.

Surviving billing, secret and database resources are critical, because they cost money or can still authenticate. Other classes are high.

Retention is an outcome, not a leak

A backup kept after termination under a stated policy is correct behaviour. Reporting it as residue would teach people to ignore the report. Setting retainedBy makes it not applicable, but it is still printed, because a retention nobody can see looks the same as a leak.

Coverage gaps

coverageGaps() lists the resource classes the teardown never mentioned. It answers a different question from "is this resource gone". A checklist that never mentions billing passes every check it makes. formatTermination() adds these gaps to the report as a NOT COVERED note.

Usage

import { verifyTermination, formatTermination, coverageGaps, type Termination } from '@extant2000/kill-switch'

const termination: Termination = {
  target: 'customer example-co',
  resources: [
    { id: 'web-1', class: 'compute', where: 'cloud console: instances',
      state: 'absent', verifiedBy: 'independent-probe' },
    { id: 'sub_123', class: 'billing', where: 'billing API: subscriptions',
      state: 'present', verifiedBy: 'independent-probe' },
    { id: 'data-vol', class: 'storage', where: 'DELETE /volumes/data-vol',
      state: 'absent', verifiedBy: 'delete-response' },
    { id: 'nightly-backup', class: 'backup', where: 's3://backups/example-co',
      state: 'present', retainedBy: '90-day retention, contractual' },
  ],
}

const report = verifyTermination(termination)   // evidence-record report
const gaps = coverageGaps(termination)          // ['dns', 'route', 'database', 'mailbox', 'secret']
console.log(formatTermination(termination))

Output of the example above, with the header, notes and detail lines omitted:

  NOT MET  ·  4 items examined  ·  5 findings

✗ CRITICAL Compute is gone and billing is still active   [NOT MET]
✗ CRITICAL billing sub_123 still present after termination   [NOT MET]
· MEDIUM   storage data-vol reported absent by the delete call itself   [NOT ASSESSED]
◦ info     compute web-1 confirmed gone by independent probe   [MET]

Resource classes are compute, storage, dns, route, database, billing, mailbox, secret and backup. An empty inventory is reported as not assessed: an unenumerated resource is not a deleted one.

Install

npm install @extant2000/kill-switch

License

MIT. See LICENSE.

S
Description
Checks that a shut-down deployment and its billing are gone.
Readme MIT
53 KiB
Languages
TypeScript 100%