# AI Trading Kill Switch

**Status:** Implemented (stub for future hardware release)
**Date:** 2026-05-12
**Migration:** 229

---

## Overview

The AI Trading Kill Switch is a platform-wide emergency stop mechanism that immediately halts all AI trading activity. When activated, all running algorithm runs are stopped on their next tick, and no new runs can be started until the kill switch is reset.

This feature is designed with future hardware button support in mind, allowing a physical emergency stop button to be connected via API.

---

## Use Cases

1. **Emergency Stop**: Immediately halt all AI trading if algorithms behave unexpectedly
2. **Market Events**: Pause trading during extreme market volatility or flash crashes
3. **System Issues**: Stop trading during platform maintenance or detected anomalies
4. **Regulatory Compliance**: Demonstrate ability to halt automated trading on demand
5. **Hardware Integration**: Physical "big red button" for trading desks (future release)

---

## Architecture

```
┌─────────────────────────────────────────────────────────────────────┐
│                     KILL SWITCH TRIGGERS                             │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │   Admin     │  │  Hardware   │  │   License   │  │   Circuit   │ │
│  │   Panel     │  │   Button    │  │   Server    │  │   Breaker   │ │
│  │ (manual)    │  │  (API key)  │  │  (remote)   │  │   (auto)    │ │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘ │
│         │                │                │                │         │
│         └────────────────┴────────────────┴────────────────┘         │
│                                   │                                  │
│                                   ▼                                  │
│         ┌─────────────────────────────────────────────────┐         │
│         │           AI Kill Switch Service                 │         │
│         │                                                  │         │
│         │  • isAiTradingKilled() - <100ms check            │         │
│         │  • triggerKillSwitch(source, reason, actor)      │         │
│         │  • resetKillSwitch(actor)                        │         │
│         │  • authenticateHardwareButton(buttonId, key)     │         │
│         └──────────────────────┬──────────────────────────┘         │
│                                │                                     │
│                                ▼                                     │
│         ┌─────────────────────────────────────────────────┐         │
│         │            Paper/Live Executor                   │         │
│         │                                                  │         │
│         │  startRun() - blocks if killed                   │         │
│         │  tickRun()  - stops run if killed                │         │
│         └─────────────────────────────────────────────────┘         │
└─────────────────────────────────────────────────────────────────────┘
```

---

## Database Schema

### Migration: `229_ai_trading_kill_switch.sql`

```sql
-- Kill switch events table
CREATE TABLE IF NOT EXISTS trading.ai_kill_switch (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Kill event
  is_killed BOOLEAN NOT NULL DEFAULT true,
  source VARCHAR(50) NOT NULL,           -- 'admin' | 'hardware' | 'license_server' | 'circuit_breaker'
  reason TEXT,

  -- Actor
  triggered_by_user_id UUID REFERENCES auth.users(id),
  triggered_by_hardware_id UUID,

  -- Timestamps
  triggered_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  reset_at TIMESTAMPTZ,
  reset_by_user_id UUID REFERENCES auth.users(id)
);

-- Hardware button registry
CREATE TABLE IF NOT EXISTS trading.kill_switch_hardware (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Button identity
  name VARCHAR(100) NOT NULL,
  description TEXT,

  -- Authentication (hashed API key)
  api_key_hash VARCHAR(128) NOT NULL,
  api_key_last4 VARCHAR(4) NOT NULL,

  -- Status
  is_active BOOLEAN NOT NULL DEFAULT true,
  last_used_at TIMESTAMPTZ,

  -- Metadata
  location VARCHAR(200),
  registered_by_user_id UUID NOT NULL REFERENCES auth.users(id),

  -- Timestamps
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Quick status view
CREATE OR REPLACE VIEW trading.ai_kill_switch_status AS
SELECT
  COALESCE(
    (SELECT is_killed FROM trading.ai_kill_switch
     ORDER BY triggered_at DESC LIMIT 1),
    false
  ) as is_killed,
  (SELECT triggered_at FROM trading.ai_kill_switch
   WHERE is_killed = true AND reset_at IS NULL
   ORDER BY triggered_at DESC LIMIT 1) as killed_since,
  (SELECT source FROM trading.ai_kill_switch
   WHERE is_killed = true AND reset_at IS NULL
   ORDER BY triggered_at DESC LIMIT 1) as source,
  (SELECT reason FROM trading.ai_kill_switch
   WHERE is_killed = true AND reset_at IS NULL
   ORDER BY triggered_at DESC LIMIT 1) as reason;
```

---

## Service Layer

### Location: `packages/be/src/trading/services/ai-kill-switch.ts`

### Key Functions

```typescript
// Check if AI trading is killed (optimized for <100ms response)
export async function isAiTradingKilled(): Promise<boolean>

// Get detailed kill switch status
export async function getKillSwitchStatus(): Promise<{
  isKilled: boolean;
  killedSince: string | null;
  source: string | null;
  reason: string | null;
}>

// Trigger the kill switch
export async function triggerKillSwitch(
  source: 'admin' | 'hardware' | 'license_server' | 'circuit_breaker',
  reason: string,
  actorUserId?: string,
  hardwareButtonId?: string
): Promise<{ success: boolean; eventId: string }>

// Reset the kill switch (resume trading)
export async function resetKillSwitch(
  actorUserId: string
): Promise<{ success: boolean }>

// Authenticate a hardware button
export async function authenticateHardwareButton(
  buttonId: string,
  apiKey: string
): Promise<{ valid: boolean; button?: HardwareButton }>

// Register a new hardware button
export async function registerHardwareButton(
  name: string,
  description: string,
  location: string,
  registeredByUserId: string
): Promise<{ buttonId: string; apiKey: string }>

// Get kill switch history
export async function getKillSwitchHistory(
  limit?: number
): Promise<KillSwitchEvent[]>
```

---

## API Endpoints

| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | `/api/predict/v1/ai-kill-switch/status` | Admin | Get current kill switch status |
| POST | `/api/predict/v1/ai-kill-switch/trigger` | Admin | Trigger kill switch (admin) |
| POST | `/api/predict/v1/ai-kill-switch/reset` | Admin | Reset kill switch |
| GET | `/api/predict/v1/ai-kill-switch/history` | Admin | Get kill switch event history |
| POST | `/api/predict/v1/ai-kill-switch/hardware/register` | Super Admin | Register hardware button |
| GET | `/api/predict/v1/ai-kill-switch/check` | Public | Quick status check for hardware |

### Hardware Button Endpoint

```bash
# Quick check (for hardware polling)
curl https://predict.agencio.cloud/api/predict/v1/ai-kill-switch/check

# Response
{ "isKilled": false }

# Hardware button trigger
curl -X POST https://predict.agencio.cloud/api/predict/v1/ai-kill-switch/trigger \
  -H "Content-Type: application/json" \
  -d '{
    "source": "hardware",
    "reason": "Physical emergency stop button pressed",
    "hardwareButtonId": "uuid-here",
    "apiKey": "hardware-api-key-here"
  }'
```

---

## Executor Integration

### startRun()

When a user attempts to start a new algorithm run, the executor checks the kill switch:

```typescript
export async function startRun(params: StartRunParams): Promise<{ runId: string }> {
  // Check platform-wide AI trading kill switch
  const killed = await isAiTradingKilled();
  if (killed) {
    throw new Error('AI trading is currently disabled by platform kill switch. Contact admin to reset before starting new algorithm runs.');
  }
  // ... rest of startRun logic
}
```

### tickRun()

On each tick of a running algorithm, the kill switch is checked:

```typescript
export async function tickRun(runId: string): Promise<TickResult> {
  const state = await hydrateRunState(runId);
  if (!state) return { ok: false, reason: 'Run not found or already ended' };

  // Check platform-wide AI trading kill switch
  const killed = await isAiTradingKilled();
  if (killed) {
    // Stop the run gracefully - record as guardrail kill
    await recordKill({
      userId: state.userId,
      runId: state.runId,
      algorithmId: state.algorithmId,
      triggeredBy: 'guardrail',
      layer: 'L1',
      reason: 'Platform AI trading kill switch activated',
    });
    // ... update run as ended
    return { ok: true, action: 'killed:L1:platform_kill_switch' };
  }
  // ... rest of tickRun logic
}
```

---

## Hardware Button Integration (Future)

### Physical Button Requirements

1. **Network connectivity**: WiFi or Ethernet to reach the API
2. **Microcontroller**: ESP32, Raspberry Pi, or similar with HTTP client capability
3. **Power**: USB or battery backup for reliability
4. **Physical design**: Large, red, covered button (industrial e-stop style)

### Example Arduino/ESP32 Code

```cpp
#include <WiFi.h>
#include <HTTPClient.h>

const char* API_ENDPOINT = "https://predict.agencio.cloud/api/predict/v1/ai-kill-switch/trigger";
const char* BUTTON_ID = "your-button-uuid";
const char* API_KEY = "your-api-key";

void triggerKillSwitch() {
  HTTPClient http;
  http.begin(API_ENDPOINT);
  http.addHeader("Content-Type", "application/json");

  String payload = "{\"source\":\"hardware\",\"reason\":\"Physical button pressed\",\"hardwareButtonId\":\"";
  payload += BUTTON_ID;
  payload += "\",\"apiKey\":\"";
  payload += API_KEY;
  payload += "\"}";

  int httpCode = http.POST(payload);
  // Handle response...
  http.end();
}
```

---

## Kill Switch Sources

| Source | Description | Typical Use |
|--------|-------------|-------------|
| `admin` | Manual trigger via admin panel | Emergency stop by operations team |
| `hardware` | Physical button press | Trading desk emergency stop |
| `license_server` | Remote kill from license server | License violation enforcement |
| `circuit_breaker` | Automatic trigger (future) | Anomaly detection auto-stop |

---

## Response Time

The kill switch is designed for sub-100ms response time to support hardware button use cases:

- **Status check**: ~10-20ms (single indexed query)
- **Trigger**: ~50-80ms (single insert + potential run stops happen async on next tick)
- **Hardware auth**: ~30-50ms (single indexed query with hash comparison)

---

## Security Considerations

1. **API Key Hashing**: Hardware button API keys are stored hashed (SHA-256)
2. **Admin Only**: Trigger and reset endpoints require administrator role
3. **Audit Trail**: All kill switch events are logged with actor, timestamp, and reason
4. **Rate Limiting**: Hardware endpoints should be rate-limited to prevent abuse

---

## Admin UI Integration (Future)

The kill switch will be integrated into the admin panel:

1. **Dashboard Widget**: Shows current kill switch status (green/red indicator)
2. **Quick Trigger Button**: One-click to trigger kill switch
3. **Reset Confirmation**: Two-step confirmation to reset (prevent accidental resume)
4. **History View**: Table of all kill switch events with filters
5. **Hardware Management**: Register/deactivate hardware buttons

---

## Testing

### Trigger Kill Switch

```bash
# Admin trigger
curl -X POST http://localhost:3333/api/predict/v1/ai-kill-switch/trigger \
  -H "Authorization: Bearer <admin-token>" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Testing kill switch"}'
```

### Check Status

```bash
curl http://localhost:3333/api/predict/v1/ai-kill-switch/status \
  -H "Authorization: Bearer <admin-token>"
```

### Reset Kill Switch

```bash
curl -X POST http://localhost:3333/api/predict/v1/ai-kill-switch/reset \
  -H "Authorization: Bearer <admin-token>"
```

---

## Related Documentation

- `docs/12-algorithm-builder.md` — AI Algorithm Builder overview
- `docs/46-ai-trader-management.md` — Individual run management
- `docs/40-ai-trader-compliance-and-audit.md` — Compliance and audit trail
