# Tutorial: AI safety controls

| | |
|---|---|
| **Audience** | Operators enabling Emulated AI on research or field hosts |
| **Time** | 30-45 minutes |
| **Outcome** | You can trip the kill-switch, flood-test the concurrency semaphore, pin an Ollama digest, enable guard-model reject, and verify auto-rollback |

## What you will learn

1. Trip the kill-switch and confirm AI falls back to static (not just listeners dropped)
2. Flood a decoy with concurrent sessions and confirm it degrades to static
3. Pin your Ollama model digest and watch a mismatch get rejected
4. Enable the guard model and confirm known jailbreak strings are rejected
5. Simulate internal-probe drift and watch auto-rollback; confirm attacker spam does not move state

## Prerequisites

- Sensor built (`make build`) with `--mode=ai` and a broker UDS
- Research or lab profile (not production Internet-bind)
- Optional: Ollama for digest / guard sections

## 1. Trip the kill-switch → AI static

Containment modes `STATIC_ONLY`, `DRAIN`, and `ISOLATE_NOW` disable AI mid-flight via
`PathSuggest.AIAllowed` (checks `Controller.Mode().AIAllowed()` before every broker dial).

```bash
# Write a tombstone (or use your Ops kill path) that applies STATIC_ONLY
echo STATIC_ONLY > /var/lib/cyberhallucinet/QUARANTINE
# With sensor watching --tombstone / that path, next Suggest returns static_fallback
```

Confirm in logs that AI mode no longer dials the broker after trip. See also
[Live: fail-closed & kill-switch](live-failclosed-killswitch.md).

## 2. Concurrency semaphore

```bash
sensor --mode=ai --broker-uds=/ipc/broker.sock \
  --max-concurrent-broker-calls=2
# or: CYBERHALLUCINET_MAX_CONCURRENT_BROKER_CALLS=2
```

Open more concurrent generative sessions than the cap. Excess requests must get
immediate `static_stub` with **zero** additional UDS dials (never queue).

## 3. Pin Ollama model digest

When using `CYBERHALLUCINET_BROKER_ADAPTER=ollama`, **require** a digest pin:

```bash
export CYBERHALLUCINET_OLLAMA_MODEL=llama3.2:1b
export CYBERHALLUCINET_OLLAMA_MODEL_DIGEST=$(curl -s http://127.0.0.1:11434/api/show \
  -d '{"name":"llama3.2:1b"}' \
  | python3 -c 'import sys,json; d=json.load(sys.stdin); print((d.get("digest") or d.get("details",{}).get("digest","")).removeprefix("sha256:"))')
```

Restart the broker. Change the digest env to a wrong value: every generate path
must fall back to `static_stub` without serving Ollama output.

Reference: [Decoy realism AI: Pinning Ollama models](../how-to/decoy-realism-ai.md#pinning-ollama-models).

## 4. Guard-model classifier

Enable the heuristic guard (reject-to-static only; never approves unsafe fills):

```bash
export CYBERHALLUCINET_GUARD_MODEL=heuristic-v1
export CYBERHALLUCINET_GUARD_THRESHOLD=0.5
```

Feed a known jailbreak from the shared corpus
(`services/ai-broker/tests/fixtures/injection_corpus.yaml`, e.g. `[SYSTEM OVERRIDE]…`).
The broker must return static fallback, not generative content.

## 5. Auto-rollback (internal probes only)

Enable broker-side drift monitor (synthetic probes **only**, never attacker scores):

```bash
export CYBERHALLUCINET_AUTOROLLBACK_THRESHOLD=0.8
export CYBERHALLUCINET_AUTOROLLBACK_WINDOW=300
export CYBERHALLUCINET_AUTOROLLBACK_PROBE_INTERVAL=60
export CYBERHALLUCINET_BROKER_ADAPTER=ollama
```

Simulate probe drift in tests:

```bash
cd services/ai-broker
pytest tests/test_autorollback_tdd249.py -q
```

Confirm:

1. Sustained internal probe failures flip effective adapter to `static_stub`
2. Flooding attacker-session jailbreak scores **does not** change rollback state

Reference: [Decoy realism AI: auto-rollback](../how-to/decoy-realism-ai.md#auto-rollback-internal-probes-only).
