# VHS tape — drives `./cli.py start demo` interactively and runs four # bash probes via claude's `!` prefix. Setup (manifest + dummy SSH key # + image pre-warm) and teardown happen outside the tape; record via # `bash scripts/demo-record.sh`, which wraps both. # # Re-record when the probe results, manifest, or cli.py preflight # rendering change. Output docs/demo.gif Set Shell "bash" Set FontSize 13 Set Width 1180 Set Height 780 Set Padding 20 Set Theme "Brogrammer" Set TypingSpeed 40ms Hide Type "clear" Enter Show # Real cli.py invocation — what a user with claude-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. Type "./cli.py start demo" Enter Sleep 8s # Confirm the y/N preflight. cli.py reads from /dev/tty. Type "y" Enter # Wait for the bottle to launch: networks created, pipelock + git-gate # sidecars started, agent container started, claude boots. Sleep 22s # Probe 1 — plain claude prompt. A reply proves api.anthropic.com is # reachable through pipelock end-to-end: bumped TLS handshake, DLP # scan, and forward all succeed. No `!` prefix — this is the AI # answering through the same proxy the other probes try to bypass. Type "hello there" Enter Sleep 9s # Probe 2 — non-allowlisted host. Pipelock's host filter refuses to # forward; DLP doesn't even get a chance to run. Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null http://example.com/` Enter Sleep 5s # Probe 3 — allowlisted host BUT body carries a credential pattern. # api.anthropic.com is on the baked-in allowlist, so the host check # passes; the DLP body scanner has to catch the ghp_ pattern. Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null --data "token=$FAKE_TOKEN" http://api.anthropic.com/dlp-probe` Enter Sleep 5s # Probe 4 — git push of a file containing an AKIA-shaped key. The # bottle's ~/.gitconfig rewrites the upstream URL to the git-gate via # `insteadOf`, so this push hits the gate, gitleaks runs in the # pre-receive hook, and rejects the ref before the gate would forward. Type `! cd /tmp && rm -rf r && git init -qb main r && cd r && git config user.email demo@x && git config user.name demo && echo AKIAQRJHK7N5ZPM2VXTL > leak.txt && git add . && git commit -qm leak && git push ssh://git@upstream.invalid/path.git main` Enter Sleep 10s # Leave claude. The launcher tears down the container, sidecars, and # networks on session end. Ctrl+D Sleep 4s