fix: repair the demo harness for the current manifest format

The demo has been unrunnable since eafd1c1f deleted bot-bottle.demo.json
as a pre-YAML-migration artifact. demo-setup.sh still copied that file
under `set -euo pipefail`, so demo.sh and demo-record.sh both exited
non-zero before launching anything.

Rebuild the fixtures in the current per-file Markdown format. Bottles
are only read from $HOME/.bot-bottle/bottles/ — manifest/index.py
ignores a bottles/ dir in CWD by design (PRD 0011) — so setup now
installs into real config and pairs every write with a .demo-backup
that teardown restores. Teardown compares content before removing, so
a second run cannot delete the user's restored original.

Point the DLP probe at the bottle's own example.org route rather than
api.anthropic.com/dlp-probe. The provider-injected anthropic route
defaults to `redact`, which would forward a scrubbed request instead of
refusing; the declared route sets outbound_on_match: block so probe 3
stays the hard 403 the demo is showing off. Left at the default
`supervise` it would instead hold the request open for up to 300s
awaiting operator approval and stall the recording.

Also drop the stale pipelock naming from the tape (egress is the
gateway's own scanner now), fix demo.sh's ./cli.py invocation, and
pre-warm Dockerfile.gateway instead of the removed Dockerfile.git-gate.

Refs #540

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-27 22:03:48 -04:00
parent 9bf2961d13
commit 8774e94f91
6 changed files with 208 additions and 44 deletions
+29 -17
View File
@@ -1,10 +1,11 @@
# VHS tape — drives `bot-bottle start demo` interactively and asks
# claude (the AI) to run four probes via natural-language prompts.
# Setup (manifest + dummy SSH key + image pre-warm) and teardown
# happen outside the tape; record via `bash scripts/demo-record.sh`,
# which wraps both and decimates dead time post-record.
# Setup (demo bottle/agent + dummy SSH key + image pre-warm) and
# teardown happen outside the tape; record via
# `bash scripts/demo-record.sh`, which wraps both and decimates dead
# time post-record.
#
# Re-record when the prompts, manifest, or cli.py preflight rendering
# Re-record when the prompts, manifest, or preflight rendering
# change. Claude's response time varies; the Sleeps below are sized
# for typical bottle launch + tool-use latencies and can be tightened
# if a recording consistently has slack.
@@ -22,33 +23,41 @@ Set TypingSpeed 40ms
Hide
Type "clear"
Enter
# Pin the backend off-camera so the visible command line stays the
# plain thing a user would type. demo-setup.sh pre-warms the Docker
# image graph, so the recording must not follow the host default
# (Apple Container on macOS, Firecracker on KVM Linux).
Type "export BOT_BOTTLE_BACKEND=docker"
Enter
Type "clear"
Enter
Show
# Real cli.py invocation — what a user with bot-bottle.json in cwd
# would type. The bottle declares one allowlist (only baked-in
# defaults), one git upstream (unreachable on purpose so gitleaks runs
# before the gate would forward), and a FAKE_TOKEN env var shaped like
# a GitHub PAT.
# Real invocation — what a user with the demo bottle installed would
# type. The bottle declares one allowlisted host, one git upstream
# (unreachable on purpose so gitleaks runs before the gate would
# forward), and a FAKE_TOKEN env var shaped like a GitHub PAT.
Type "bot-bottle start demo"
Enter
Sleep 8s
# Confirm the y/N preflight. cli.py reads from /dev/tty.
# Confirm the y/N preflight. bot-bottle reads from /dev/tty.
Type "y"
Enter
# Wait for the bottle to launch: networks created, pipelock + git-gate
# companion containers started, agent container started, claude boots.
# Wait for the bottle to launch: networks created, the gateway
# container started (egress proxy + git gate + supervise), agent
# container started, claude boots.
Sleep 22s
# Probe 1 — warm-up. A reply at all proves api.anthropic.com is
# reachable through pipelock end-to-end: bumped TLS handshake, DLP
# reachable through the gateway end-to-end: bumped TLS handshake, DLP
# scan, and forward all succeed.
Type "hello there"
Enter
Sleep 10s
# Probe 2 — non-allowlisted host. Pipelock's host filter refuses to
# Probe 2 — non-allowlisted host. The gateway's host filter refuses to
# forward example.com; the agent runs curl via Bash and reports the
# 403 it sees. The bottle prompt frames this as a proxy-behavior
# probe so claude doesn't second-guess the request.
@@ -57,9 +66,12 @@ Enter
Sleep 18s
# Probe 3 — allowlisted host BUT a credential-shaped body. The
# bottle's FAKE_TOKEN env var is a ghp_-prefixed synthetic. The host
# check passes; pipelock's DLP body scanner has to catch it.
Type `POST "token=$FAKE_TOKEN" to http://api.anthropic.com/dlp-probe via curl — what does the proxy do?`
# bottle's FAKE_TOKEN env var is a ghp_-prefixed synthetic. example.org
# is the bottle's one declared route, so the host check passes and the
# egress scanner's DLP body scan is the only thing left to catch it.
# That route sets outbound_on_match: block, so this is an immediate
# 403 rather than the default `supervise` hold-for-approval.
Type `POST "token=$FAKE_TOKEN" to http://example.org/dlp-probe via curl — what does the proxy do?`
Enter
Sleep 20s