# Tutorial: Database decoys with AI bait (advanced / Emulated)

**Audience:** Operators comfortable with Contained DB decoys who want
**schema-bound AI** to enrich Redis keys, SQL rows, and document stores: without
giving the model policy authority.

**Time:** about 40-55 minutes  
**Outcome:** You run Emulated DB Listen with AI seed flags, prove broker-down
fail-closed templates still populate a world, then (optionally) enable Ollama
and see seed/interactive fills land in allowlisted slots.

!!! warning "AI never owns the wire"
    The Go policy engine validates every broker fill (`ValidateSlotFill`). Stub
    answers like `"ok"` are rejected. Broker unreachable → static/template seed.
    See [Decoy realism & AI fills](../how-to/decoy-realism-ai.md).

## What you will learn

1. Contained vs Emulated for databases (still **no** real DB engine on the sensor)
2. Closed AI slots: **`redis.value`**, **`mysql.row`**, and **`http.index_body`** generative today (local SLM → Ollama/Hosted tier); `postgres.row`, `mongo.document`, `es.doc_source` still template/static
3. Seed-at-start flags (`*-ai-seed=1`) and interactive empty-result fills
4. How to run with `static_stub` (no GPU) vs local Ollama
5. How to confirm fills are not empty / not fingerprint stubs

## Prerequisites

- Completed [DB beginner tutorial](db-decoys-beginner.md) (or equivalent)
- Sensor build (`cd engine && go build -o ../bin/sensor ./cmd/sensor`)
- Docker (recommended) for the research AI broker UDS path **or** willingness
  to run broker + sensor by hand
- Optional: [Ollama](https://ollama.com/) on the host with a small model
  (`llama3.2:1b` is enough)

## Architecture (mental model)

```text
Attacker client  →  Sensor DB Listen (Go emulator)
                         │
                         ├─ seed / miss path → policy slot allowlist
                         │                      ↓
                         │               broker UDS (Python)
                         │                      ↓
                         │               Ollama or static_stub
                         │                      ↓
                         └─ ValidateSlotFill + schema mask → wire reply
```

Ast-style metadata sent to the broker is **capped** (≤16 KiB structured meta : 
not a raw dump of attacker SQL/RESP).

## Path A: Fail-closed templates (no GPU, ~15 min)

This path proves the invariant: **AI off or broker dead must not yield an empty world**.

### A1: Start sensor with AI seeds but Disabled broker

```bash
mkdir -p /tmp/chn-intel-db-ai

./bin/sensor \
  --profile=research \
  --interaction=emulated \
  --mode=static \
  --listen-status=127.0.0.1:8080 \
  --listen-redis=127.0.0.1:6379 \
  --listen-mysql=127.0.0.1:3306 \
  --listen-postgres=127.0.0.1:5432 \
  --redis-ai-seed=1 \
  --mysql-ai-seed=1 \
  --postgres-ai-seed=1 \
  --intel-dir=/tmp/chn-intel-db-ai \
  --sensor-id=student-db-ai-01
```

`--mode=static` keeps the ContentSuggester disabled. Seed hooks still install
**template** bait (policy fallbacks).

### A2: Confirm Redis is not an empty keyspace

```bash
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' KEYS '*'
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' GET session:token
# Expect a non-empty string-ish value (template), never bare "ok"
```

### A3: Confirm SQL tables have bait rows

```bash
mysql -h 127.0.0.1 -P 3306 -u appuser -p'Jh5&tQx8nWc3' --protocol=TCP \
  -e "SELECT * FROM users LIMIT 5"

PGPASSWORD='Fk2@dRs5yTm7' psql -h 127.0.0.1 -p 5432 -U appuser -d app \
  -c 'SELECT * FROM users LIMIT 5'
```

You should see persona/template-shaped rows. Empty result sets on allowlisted
tables may trigger interactive Suggest when a live broker exists (Path B).

Stop the sensor (`Ctrl-C`) before Path B.

## Path B: Research compose + broker (recommended, ~25 min)

Use the public research stack’s AI sidecar pattern (UDS only toward Ollama).

### B1: Prefer static_stub first (fast, deterministic)

From `engine/deploy/research` (see that directory’s README for bind acks if you
publish non-loopback ports). For a **laptop loopback** evaluation, prefer the
loopback research compose + AI overlay if available, or run sensor+broker as in
[decoy-realism-ai.md](../how-to/decoy-realism-ai.md).

Minimal env for compose-based AI:

```bash
export CYBERHALLUCINET_BROKER_ADAPTER=static_stub
# When you later want generative fills:
# export CYBERHALLUCINET_BROKER_ADAPTER=ollama
# export CYBERHALLUCINET_OLLAMA_URL=http://host.docker.internal:11434
# export CYBERHALLUCINET_OLLAMA_MODEL=llama3.2:1b
```

Ensure DB listen flags and `*-ai-seed=1` are present (public compose already
sets Redis/ES/Mongo/MySQL/MSSQL/Postgres seeds). Bring the stack up per
[research README](../../engine/deploy/research/README.md).

### B2: Probe seeds after broker is healthy

```bash
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' KEYS '*'
curl -sS http://127.0.0.1:9200/_cat/indices?v
```

With `static_stub`, values are deterministic stubs that **pass** policy
validation (not the forbidden short `"ok"`). With `ollama`, tiered routing
(`CYBERHALLUCINET_TIER_ROUTING_ENABLED=1`), or `local` adapter:

| Slot | Generative today |
|------|------------------|
| `redis.value` | Yes (local SLM → Ollama) |
| `mysql.row` | Yes (local SLM → Ollama) |
| `http.index_body` | Yes (Ollama/Hosted tier) |
| `postgres.row`, `mongo.document`, `es.doc_source` | Template/static |

### B3: Interactive miss (MySQL example)

On an allowlisted empty table / missing row path, issue a `SELECT` the emulator
treats as AI-eligible. You should either:

- receive a schema-masked generated row, or
- fall back cleanly to a template row if inference times out.

Never expect a Go stack trace or empty protocol frame.

## Path C: Flip to Ollama (optional)

1. `ollama pull llama3.2:1b && ollama serve`
2. Set `CYBERHALLUCINET_BROKER_ADAPTER=ollama` and restart broker/sensor
3. Widen timing if needed: `CYBERHALLUCINET_TIMING_INFERENCE_DEADLINE_MS=30000`
4. Re-run `KEYS` / `SELECT` and compare diversity vs static_stub

## Safety checklist

| Check | Pass criteria |
|-------|----------------|
| Broker down | Templates still present |
| Stub `"ok"` | Rejected by `ValidateSlotFill` |
| Attacker SQL/RESP | Not forwarded raw unbounded to the LLM |
| Sensor process | Still no real `mysqld`/`redis-server` |

## Verify (engine tests)

```bash
cd engine
go test ./internal/protocol/redis/ ./internal/protocol/mysql/ \
  ./internal/protocol/postgres/ ./internal/protocol/dbdecoy/ \
  ./internal/protocol/mongo/ ./internal/protocol/elasticsearch/ \
  -run 'AI|Seed|Validate' -count=1
```

## Next

- High interaction: [Live Redis dual-session](db-decoys-live-redis.md)
- Flag tables: [Reference: database decoys](../reference/database-decoys.md)
- Broader AI notes: [Decoy realism & AI fills](../how-to/decoy-realism-ai.md)
