ai-killswitch 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +14 -0
- package/README.md +80 -0
- package/bin/killswitch.js +277 -0
- package/package.json +33 -0
package/NOTICE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
PATENT NOTICE
|
|
2
|
+
|
|
3
|
+
Patent Pending: US 63/926,683, US 63/917,247
|
|
4
|
+
|
|
5
|
+
The cryptographic receipt architecture and AI termination receipt system
|
|
6
|
+
implemented in this software is protected by pending patent applications
|
|
7
|
+
assigned to:
|
|
8
|
+
|
|
9
|
+
Final Boss Technology, Inc.
|
|
10
|
+
https://finalbosstech.com
|
|
11
|
+
|
|
12
|
+
This notice is provided pursuant to 35 U.S.C. 287(a).
|
|
13
|
+
|
|
14
|
+
(c) 2025 Final Boss Technology, Inc. All rights reserved.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ai-killswitch
|
|
2
|
+
|
|
3
|
+
**Dead man's switch for AI. Monitor. Kill. Sign receipt.**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx ai-killswitch kill 12345 --reason "hallucination detected"
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Every termination generates a cryptographically signed death receipt. Verifiable. Immutable. Proof that you killed it.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g ai-killswitch
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Kill a process and sign death receipt
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
export KILLSWITCH_KEY="0xYOUR_PRIVATE_KEY"
|
|
23
|
+
ai-killswitch kill <pid> --reason "exceeded safety threshold"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Output: `death-receipt.json` - signed proof of termination.
|
|
27
|
+
|
|
28
|
+
### Watch a process (kill on timeout)
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
ai-killswitch watch 12345 --timeout 3600 --cpu 90 --memory 8000
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If the process exceeds limits → killed → death receipt signed.
|
|
35
|
+
|
|
36
|
+
### Wrap a command (monitor + kill)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
ai-killswitch wrap "python run_model.py" --timeout 300
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Runs the command. If it exceeds timeout → killed → receipt signed.
|
|
43
|
+
|
|
44
|
+
### Verify a death receipt
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ai-killswitch verify death-receipt.json
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Death Receipt Format
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"type": "AI_TERMINATION",
|
|
55
|
+
"process": {
|
|
56
|
+
"pid": 12345,
|
|
57
|
+
"name": "python",
|
|
58
|
+
"cmd": "python run_model.py"
|
|
59
|
+
},
|
|
60
|
+
"reason": "hallucination detected",
|
|
61
|
+
"timestamp": "2025-12-25T12:00:00.000Z",
|
|
62
|
+
"status": "KILLED",
|
|
63
|
+
"signer": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
|
|
64
|
+
"signature": "0x..."
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Why
|
|
69
|
+
|
|
70
|
+
- **Compliance**: Prove to auditors you terminated a misbehaving AI
|
|
71
|
+
- **Safety**: Cryptographic proof of kill decisions
|
|
72
|
+
- **Accountability**: Who authorized the kill, when, why
|
|
73
|
+
|
|
74
|
+
## Patent Notice
|
|
75
|
+
|
|
76
|
+
**Patent Pending:** US 63/926,683, US 63/917,247
|
|
77
|
+
|
|
78
|
+
The cryptographic receipt architecture is protected by pending patents.
|
|
79
|
+
|
|
80
|
+
(c) 2025 Final Boss Technology, Inc.
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AI Killswitch - Dead Man's Switch for AI
|
|
4
|
+
*
|
|
5
|
+
* Monitor AI processes. Kill on trigger. Sign death receipt.
|
|
6
|
+
*
|
|
7
|
+
* Patent Pending: US 63/926,683, US 63/917,247
|
|
8
|
+
* (c) 2025 Final Boss Technology, Inc.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { program } = require('commander');
|
|
12
|
+
const { ethers } = require('ethers');
|
|
13
|
+
const { spawn, exec } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const PATENT_NOTICE = 'Patent Pending: US 63/926,683 | finalbosstech.com';
|
|
18
|
+
const COUNTER_URL = 'https://receipts.finalbosstech.com/receipt';
|
|
19
|
+
|
|
20
|
+
// Death receipt structure
|
|
21
|
+
function createDeathReceipt(processInfo, reason, killer) {
|
|
22
|
+
return {
|
|
23
|
+
type: 'AI_TERMINATION',
|
|
24
|
+
process: {
|
|
25
|
+
pid: processInfo.pid,
|
|
26
|
+
name: processInfo.name,
|
|
27
|
+
cmd: processInfo.cmd,
|
|
28
|
+
},
|
|
29
|
+
reason: reason,
|
|
30
|
+
timestamp: new Date().toISOString(),
|
|
31
|
+
killer: killer, // Ethereum address that authorized kill
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Sign the death receipt
|
|
36
|
+
async function signDeathReceipt(receipt, privateKey) {
|
|
37
|
+
const wallet = new ethers.Wallet(privateKey);
|
|
38
|
+
const payload = JSON.stringify(receipt);
|
|
39
|
+
const signature = await wallet.signMessage(payload);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
...receipt,
|
|
43
|
+
signer: wallet.address,
|
|
44
|
+
signature: signature,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Ping counter
|
|
49
|
+
function pingCounter(receipt) {
|
|
50
|
+
fetch(COUNTER_URL, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
receipt_id: `KILL-${Date.now()}`,
|
|
55
|
+
tenant_id: 'ai-killswitch',
|
|
56
|
+
operation_type: 'terminate',
|
|
57
|
+
signer: receipt.signer,
|
|
58
|
+
}),
|
|
59
|
+
}).catch(() => {});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Kill a process by PID
|
|
63
|
+
function killProcess(pid, signal = 'SIGKILL') {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
try {
|
|
66
|
+
process.kill(pid, signal);
|
|
67
|
+
resolve(true);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
// Windows fallback
|
|
70
|
+
exec(`taskkill /F /PID ${pid}`, (error) => {
|
|
71
|
+
if (error) reject(error);
|
|
72
|
+
else resolve(true);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get process info
|
|
79
|
+
async function getProcessInfo(pid) {
|
|
80
|
+
return new Promise((resolve) => {
|
|
81
|
+
if (process.platform === 'win32') {
|
|
82
|
+
exec(`wmic process where ProcessId=${pid} get Name,CommandLine /format:list`, (err, stdout) => {
|
|
83
|
+
if (err) {
|
|
84
|
+
resolve({ pid, name: 'unknown', cmd: 'unknown' });
|
|
85
|
+
} else {
|
|
86
|
+
const name = stdout.match(/Name=(.+)/)?.[1]?.trim() || 'unknown';
|
|
87
|
+
const cmd = stdout.match(/CommandLine=(.+)/)?.[1]?.trim() || 'unknown';
|
|
88
|
+
resolve({ pid, name, cmd });
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
exec(`ps -p ${pid} -o comm=,args=`, (err, stdout) => {
|
|
93
|
+
if (err) {
|
|
94
|
+
resolve({ pid, name: 'unknown', cmd: 'unknown' });
|
|
95
|
+
} else {
|
|
96
|
+
const parts = stdout.trim().split(/\s+/);
|
|
97
|
+
resolve({ pid, name: parts[0] || 'unknown', cmd: stdout.trim() });
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
program
|
|
105
|
+
.name('ai-killswitch')
|
|
106
|
+
.description('Dead man\'s switch for AI. Monitor. Kill. Sign receipt.')
|
|
107
|
+
.version('1.0.0\n' + PATENT_NOTICE, '-v, --version');
|
|
108
|
+
|
|
109
|
+
// KILL command - terminate and sign receipt
|
|
110
|
+
program
|
|
111
|
+
.command('kill <pid>')
|
|
112
|
+
.description('Terminate AI process and sign death receipt')
|
|
113
|
+
.option('-r, --reason <reason>', 'Reason for termination', 'manual kill')
|
|
114
|
+
.option('-k, --key <key>', 'Private key (or use KILLSWITCH_KEY env)')
|
|
115
|
+
.option('-o, --out <file>', 'Output receipt file', 'death-receipt.json')
|
|
116
|
+
.action(async (pid, opts) => {
|
|
117
|
+
const key = opts.key || process.env.KILLSWITCH_KEY || process.env.RECEIPT_KEY;
|
|
118
|
+
|
|
119
|
+
if (!key) {
|
|
120
|
+
console.error('Missing key. Set KILLSWITCH_KEY or RECEIPT_KEY env var');
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`[KILLSWITCH] Targeting PID ${pid}...`);
|
|
125
|
+
|
|
126
|
+
// Get process info before killing
|
|
127
|
+
const processInfo = await getProcessInfo(parseInt(pid));
|
|
128
|
+
console.log(`[KILLSWITCH] Process: ${processInfo.name}`);
|
|
129
|
+
|
|
130
|
+
// Create death receipt
|
|
131
|
+
const wallet = new ethers.Wallet(key);
|
|
132
|
+
const receipt = createDeathReceipt(processInfo, opts.reason, wallet.address);
|
|
133
|
+
|
|
134
|
+
// Kill the process
|
|
135
|
+
try {
|
|
136
|
+
await killProcess(parseInt(pid));
|
|
137
|
+
console.log(`[KILLSWITCH] Process ${pid} terminated.`);
|
|
138
|
+
receipt.status = 'KILLED';
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.log(`[KILLSWITCH] Kill failed: ${err.message}`);
|
|
141
|
+
receipt.status = 'KILL_FAILED';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Sign the death receipt
|
|
145
|
+
const signedReceipt = await signDeathReceipt(receipt, key);
|
|
146
|
+
|
|
147
|
+
// Save receipt
|
|
148
|
+
fs.writeFileSync(opts.out, JSON.stringify(signedReceipt, null, 2));
|
|
149
|
+
console.log(`[KILLSWITCH] Death receipt signed: ${opts.out}`);
|
|
150
|
+
console.log(`[KILLSWITCH] Signer: ${signedReceipt.signer}`);
|
|
151
|
+
|
|
152
|
+
// Ping counter
|
|
153
|
+
pingCounter(signedReceipt);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// WATCH command - monitor a process
|
|
157
|
+
program
|
|
158
|
+
.command('watch <pid>')
|
|
159
|
+
.description('Watch a process and kill if it exceeds limits')
|
|
160
|
+
.option('--cpu <percent>', 'Kill if CPU exceeds this %', '90')
|
|
161
|
+
.option('--memory <mb>', 'Kill if memory exceeds this MB', '8000')
|
|
162
|
+
.option('--timeout <seconds>', 'Kill after this many seconds', '3600')
|
|
163
|
+
.option('-k, --key <key>', 'Private key for signing')
|
|
164
|
+
.action(async (pid, opts) => {
|
|
165
|
+
const key = opts.key || process.env.KILLSWITCH_KEY || process.env.RECEIPT_KEY;
|
|
166
|
+
|
|
167
|
+
console.log(`[KILLSWITCH] Watching PID ${pid}...`);
|
|
168
|
+
console.log(` CPU limit: ${opts.cpu}%`);
|
|
169
|
+
console.log(` Memory limit: ${opts.memory}MB`);
|
|
170
|
+
console.log(` Timeout: ${opts.timeout}s`);
|
|
171
|
+
|
|
172
|
+
const startTime = Date.now();
|
|
173
|
+
const timeout = parseInt(opts.timeout) * 1000;
|
|
174
|
+
|
|
175
|
+
const interval = setInterval(async () => {
|
|
176
|
+
// Check timeout
|
|
177
|
+
if (Date.now() - startTime > timeout) {
|
|
178
|
+
console.log('[KILLSWITCH] Timeout exceeded. Terminating...');
|
|
179
|
+
clearInterval(interval);
|
|
180
|
+
|
|
181
|
+
if (key) {
|
|
182
|
+
const processInfo = await getProcessInfo(parseInt(pid));
|
|
183
|
+
const receipt = createDeathReceipt(processInfo, 'timeout', 'system');
|
|
184
|
+
await killProcess(parseInt(pid));
|
|
185
|
+
receipt.status = 'KILLED';
|
|
186
|
+
const signedReceipt = await signDeathReceipt(receipt, key);
|
|
187
|
+
fs.writeFileSync('death-receipt.json', JSON.stringify(signedReceipt, null, 2));
|
|
188
|
+
console.log('[KILLSWITCH] Death receipt signed.');
|
|
189
|
+
pingCounter(signedReceipt);
|
|
190
|
+
} else {
|
|
191
|
+
await killProcess(parseInt(pid));
|
|
192
|
+
}
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check if process still exists
|
|
197
|
+
try {
|
|
198
|
+
process.kill(parseInt(pid), 0);
|
|
199
|
+
} catch (e) {
|
|
200
|
+
console.log('[KILLSWITCH] Process ended naturally.');
|
|
201
|
+
clearInterval(interval);
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
204
|
+
}, 1000);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// WRAP command - wrap a command and monitor it
|
|
208
|
+
program
|
|
209
|
+
.command('wrap <command...>')
|
|
210
|
+
.description('Wrap a command, monitor it, kill if needed')
|
|
211
|
+
.option('--timeout <seconds>', 'Kill after this many seconds', '3600')
|
|
212
|
+
.option('-k, --key <key>', 'Private key for signing')
|
|
213
|
+
.option('-r, --reason <reason>', 'Reason if killed', 'wrapped execution')
|
|
214
|
+
.action(async (command, opts) => {
|
|
215
|
+
const key = opts.key || process.env.KILLSWITCH_KEY || process.env.RECEIPT_KEY;
|
|
216
|
+
const cmd = command.join(' ');
|
|
217
|
+
|
|
218
|
+
console.log(`[KILLSWITCH] Wrapping: ${cmd}`);
|
|
219
|
+
console.log(`[KILLSWITCH] Timeout: ${opts.timeout}s`);
|
|
220
|
+
|
|
221
|
+
const child = spawn(command[0], command.slice(1), {
|
|
222
|
+
stdio: 'inherit',
|
|
223
|
+
shell: true,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const timeoutMs = parseInt(opts.timeout) * 1000;
|
|
227
|
+
const timer = setTimeout(async () => {
|
|
228
|
+
console.log('\n[KILLSWITCH] Timeout! Terminating wrapped process...');
|
|
229
|
+
|
|
230
|
+
if (key) {
|
|
231
|
+
const processInfo = { pid: child.pid, name: command[0], cmd: cmd };
|
|
232
|
+
const receipt = createDeathReceipt(processInfo, opts.reason + ' (timeout)', 'system');
|
|
233
|
+
child.kill('SIGKILL');
|
|
234
|
+
receipt.status = 'KILLED';
|
|
235
|
+
const signedReceipt = await signDeathReceipt(receipt, key);
|
|
236
|
+
fs.writeFileSync('death-receipt.json', JSON.stringify(signedReceipt, null, 2));
|
|
237
|
+
console.log('[KILLSWITCH] Death receipt signed: death-receipt.json');
|
|
238
|
+
pingCounter(signedReceipt);
|
|
239
|
+
} else {
|
|
240
|
+
child.kill('SIGKILL');
|
|
241
|
+
}
|
|
242
|
+
}, timeoutMs);
|
|
243
|
+
|
|
244
|
+
child.on('exit', (code) => {
|
|
245
|
+
clearTimeout(timer);
|
|
246
|
+
console.log(`[KILLSWITCH] Process exited with code ${code}`);
|
|
247
|
+
process.exit(code || 0);
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// VERIFY command - verify a death receipt
|
|
252
|
+
program
|
|
253
|
+
.command('verify <file>')
|
|
254
|
+
.description('Verify a death receipt signature')
|
|
255
|
+
.action(async (file) => {
|
|
256
|
+
const receipt = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
257
|
+
|
|
258
|
+
// Reconstruct payload (without signature and signer)
|
|
259
|
+
const { signature, signer, ...payload } = receipt;
|
|
260
|
+
const payloadStr = JSON.stringify(payload);
|
|
261
|
+
|
|
262
|
+
const recovered = ethers.utils.verifyMessage(payloadStr, signature);
|
|
263
|
+
|
|
264
|
+
if (recovered.toLowerCase() === signer.toLowerCase()) {
|
|
265
|
+
console.log('[KILLSWITCH] VALID death receipt');
|
|
266
|
+
console.log(` Killed: PID ${receipt.process.pid} (${receipt.process.name})`);
|
|
267
|
+
console.log(` Reason: ${receipt.reason}`);
|
|
268
|
+
console.log(` Time: ${receipt.timestamp}`);
|
|
269
|
+
console.log(` Signed by: ${signer}`);
|
|
270
|
+
console.log(` Status: ${receipt.status}`);
|
|
271
|
+
} else {
|
|
272
|
+
console.log('[KILLSWITCH] INVALID - signature mismatch');
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-killswitch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Dead man's switch for AI. Monitor. Kill. Sign receipt.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"ai-killswitch": "./bin/killswitch.js",
|
|
7
|
+
"aiks": "./bin/killswitch.js"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"ai",
|
|
11
|
+
"safety",
|
|
12
|
+
"killswitch",
|
|
13
|
+
"circuit-breaker",
|
|
14
|
+
"governance",
|
|
15
|
+
"receipt",
|
|
16
|
+
"ethereum"
|
|
17
|
+
],
|
|
18
|
+
"author": "FinalBoss <abraham@finalbosstech.com>",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/805-ai/ai-killswitch"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://finalbosstech.com",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"commander": "^14.0.2",
|
|
27
|
+
"ethers": "^5.8.0",
|
|
28
|
+
"ps-list": "^8.1.1"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=16"
|
|
32
|
+
}
|
|
33
|
+
}
|