# Tutorial: Live Redis dual-session (high interaction)

**Audience:** Operators who finished Contained/Emulated DB labs and understand
Live SSH gates ([first Live session](live-first-execution.md)).

**Time:** about 35-45 minutes  
**Outcome:** You prove Redis Live is a **dual-session application proxy**: the
attacker authenticates to the synthetic decoy; admitted commands run on a
**disposable guest Redis** using **proxy-held** credentials. Dangerous ops never
reach the guest.

!!! danger "Live touches a real Redis"
    The guest Redis is a real process. Use a **burnable** container/VM on an
    isolated network. Never point `--live-redis-backend` at production or shared
    data stores. Contained/Emulated never dial a real Redis; only Live does.

## What you will learn

1. Why Live Redis is **not** a post-auth byte tee
2. How attacker AUTH and guest AUTH stay separate
3. How the Live denylist answers native `-ERR` without forwarding
4. How guest death / drain fail closed to safe behavior
5. How this fits the broader Live ack model

## Prerequisites

- [DB beginner](db-decoys-beginner.md) completed
- Sensor binary built
- Docker for a disposable Redis guest
- Familiarity with Live ack string
  `CYBERHALLUCINET_LIVE_ACK=I_UNDERSTAND_LIVE_INTERACTION`

## Dual-session model

```text
┌────────────┐   AUTH decoy pw    ┌──────────────────┐
│  Attacker  │ ─────────────────► │ Sensor Redis emu │  session #1 (synthetic)
└────────────┘                    └────────┬─────────┘
                                           │ after admit
                                           │ AUTH guest pw (proxy creds)
                                           ▼
                                  ┌──────────────────┐
                                  │ Disposable Redis │  session #2 (real)
                                  └──────────────────┘
```

- Attacker never learns the guest password.
- Guest never sees the attacker password.
- `EVAL` / `SCRIPT` / `SLAVEOF` / `FLUSHALL` / `CONFIG SET` / … are denied
  **locally** with Redis-shaped errors.

## Step 1: Start a disposable guest Redis

Use a password **different** from the decoy persona password
(`R7q!kLp2vMx9` in SampleRedis):

```bash
docker rm -f chn-live-redis 2>/dev/null || true
docker run -d --name chn-live-redis \
  -p 127.0.0.1:16379:6379 \
  redis:7.2-alpine \
  redis-server --requirepass 'GuestOnly!9xQ2'

# Prove guest works with GUEST creds only
redis-cli -h 127.0.0.1 -p 16379 -a 'GuestOnly!9xQ2' PING
# PONG

# Decoy password must NOT work on the guest
redis-cli -h 127.0.0.1 -p 16379 -a 'R7q!kLp2vMx9' PING
# NOAUTH / WRONGPASS: expected
```

Seed a canary key on the guest so you can prove Live reads real data:

```bash
redis-cli -h 127.0.0.1 -p 16379 -a 'GuestOnly!9xQ2' SET live:canary 'from-guest-redis'
```

## Step 2: Start the sensor in Live with Redis backend

```bash
export CYBERHALLUCINET_LIVE_ACK=I_UNDERSTAND_LIVE_INTERACTION

./bin/sensor \
  --profile=production \
  --interaction=live \
  --listen-status=127.0.0.1:8080 \
  --listen-redis=127.0.0.1:6379 \
  --live-redis-backend='GuestOnly!9xQ2@127.0.0.1:16379' \
  --intel-dir=/tmp/chn-intel-db-live \
  --sensor-id=lab-live-redis-01
```

Notes:

- `--live-redis-backend` format is `[user:pass@]host:port`. For requirepass-only
  Redis, `password@host:port` is enough (see flag help on `--live-redis-backend`).
- If Live gates fail, the sensor serves Emulated Redis instead: check logs /
  status for downgrade reasons ([Live how-to](../how-to/live-proxy.md)).

!!! tip "Backend form"
    If your build expects `user:pass@host:port`, use
    `default:GuestOnly!9xQ2@127.0.0.1:16379`.

## Step 3: Attacker path (decoy AUTH → guest data)

Authenticate with the **decoy** password, then read the canary:

```bash
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' GET live:canary
# expect: from-guest-redis
```

That value exists only on the guest. Contained/Emulated would not return it
unless you seeded the emulator store yourself.

Write through Live and confirm on the guest:

```bash
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' SET attacker:note 'via-live'
redis-cli -h 127.0.0.1 -p 16379 -a 'GuestOnly!9xQ2' GET attacker:note
# expect: via-live
```

## Step 4: Prove the denylist (never hits the guest)

```bash
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' EVAL "return 1" 0
# expect: -ERR … Lua / not configured …

redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' SLAVEOF 1.2.3.4 6379
# expect: -ERR … not allowed …

redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' FLUSHALL
# expect: -ERR … not allowed …
```

On the guest, confirm nothing destructive happened:

```bash
redis-cli -h 127.0.0.1 -p 16379 -a 'GuestOnly!9xQ2' GET live:canary
# still: from-guest-redis
```

## Step 5, Guest death / soft fallback

```bash
docker stop chn-live-redis
redis-cli -h 127.0.0.1 -p 6379 -a 'R7q!kLp2vMx9' GET anything
# expect a native -ERR about Live backend loss (not a bare TCP drop mid-protocol)
```

Restart guest if you want another round; new Live opens may soft-fallback to
Emulated when dial fails (fail-closed posture).

## Step 6: Tear down

```bash
# Ctrl-C the sensor
docker rm -f chn-live-redis
```

## What is intentionally not covered

| Topic | Status |
|-------|--------|
| Live MySQL / Postgres / Mongo / ES dual-session | Not shipped: helpers only; use Contained/Emulated Listen |
| Post-auth raw TCP tee | **Forbidden** by design |
| Forwarding attacker AUTH to guest | **Forbidden** |

## Verify

```bash
cd engine
go test ./internal/liveproxy/ ./internal/protocol/redis/ \
  -run 'Live|Denied|Eval|SLAVEOF' -count=1
```

## Related

- [How-to: Live](../how-to/live-proxy.md) · [Reference: Live proxy](../reference/live-proxy.md)
- [Reference: database decoys](../reference/database-decoys.md)
- [Live fail-closed & kill-switch](live-failclosed-killswitch.md)
- Prior DB labs: [beginner](db-decoys-beginner.md) · [AI Emulated](db-decoys-ai-emulated.md)
