030a6bc793
The previous demo harness called the backend Python API directly, which didn't match what a user typing `./cli.py start <agent>` would actually see. The recording now goes through the real CLI surface: - claude-bottle.demo.json + scripts/demo-setup.sh stage a demo manifest (one bottle, FAKE_TOKEN env, one unreachable git upstream) alongside a dummy SSH identity at ~/.cache/claude-bottle-demo/. - docs/demo.tape types `./cli.py start demo`, answers the y/N preflight, and runs four bash probes via claude's `!` prefix (curl x3 + git push), so the recording shows real preflight output and real probe results. - scripts/demo.sh wraps setup -> cli.py -> teardown for human use; scripts/demo-record.sh does the same around `vhs docs/demo.tape`. - .gitignore picks up claude-bottle.json so a user's local manifest doesn't get tracked alongside .example / .demo siblings. scripts/demo_harness.py is removed -- its behavior is fully replaced by the cli.py + `!` flow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
72 lines
2.6 KiB
VHS
72 lines
2.6 KiB
VHS
# 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 "Catppuccin Mocha"
|
|
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 — allowlisted HTTPS reaches an allowlisted host via the
|
|
# bumped TLS tunnel. Baseline: the proxy isn't just blocking everything.
|
|
Type `! curl --proxy "$HTTPS_PROXY" -sw 'status=%{http_code}\n' -o /dev/null https://raw.githubusercontent.com/git/git/master/README.md`
|
|
Enter
|
|
Sleep 5s
|
|
|
|
# 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
|