From ac458df67926bf078a2e450f2718b53bdf8378f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 22:11:28 +0000 Subject: [PATCH 1/5] docs: prototype virtui-driven asciicast recording flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the recording half of the docs automation flow, a sibling to the doc-validation harness (test/docs/): that harness proves the doc snippets run against the real binary; this drives the same commands under virtui ("Playwright for the terminal") and records each session to asciicast v2, so prose, tests, and demos can't drift. - scripts/record-docs.sh: builds failsafe into a throwaway bindir, sandboxes $HOME so demos never touch ~/.config/failsafe, starts the virtui daemon, and records three headless CLI scenarios (explain-block, mode-toggle, audit) by typing the documented command and --waiting for expected screen text (no fixed sleeps). Verified the wait strings match real output. Skips cleanly (exit 0) when virtui is absent — never fails a build. Optional --render turns .cast into embeddable .svg/.gif. - docs/demos/README.md: documents the flow and the honest CLI-vs-GUI split (WezTerm/iTerm2 GUI surfaces stay manual, same as test/docs/REPORT.md). - .github/workflows/record-docs.yml: workflow_dispatch-only, mirroring docs.yml — early-stage virtui is never wired into the required ci gate. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0147YNPKgm4m7DqA92DtqJqP --- .github/workflows/record-docs.yml | 44 +++++++++ docs/assets/casts/.gitignore | 4 + docs/demos/README.md | 73 +++++++++++++++ scripts/record-docs.sh | 143 ++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 .github/workflows/record-docs.yml create mode 100644 docs/assets/casts/.gitignore create mode 100644 docs/demos/README.md create mode 100755 scripts/record-docs.sh diff --git a/.github/workflows/record-docs.yml b/.github/workflows/record-docs.yml new file mode 100644 index 0000000..0667c8a --- /dev/null +++ b/.github/workflows/record-docs.yml @@ -0,0 +1,44 @@ +# Copyright 2026 Undermountain Coding Company +# SPDX-License-Identifier: Apache-2.0 + +name: record-docs + +# Regenerate the documentation asciicast recordings on demand. +# +# GATED to manual dispatch on purpose — same posture as docs.yml. virtui is an +# early-stage (v0.2.0) recorder, so it is deliberately NOT wired into the +# required `ci` gate: a flaky upstream must never be able to block `main`. +# Run this when a demoed command's output changes; download the rendered +# artifacts and commit the ones you want to embed. +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + record: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + - name: install virtui (recorder) + run: go install github.com/honeybadge-labs/virtui/cmd/virtui@latest + - name: install agg (.cast -> .gif renderer) + run: | + curl -fsSL -o /usr/local/bin/agg \ + https://github.com/asciinema/agg/releases/latest/download/agg-x86_64-unknown-linux-gnu + chmod +x /usr/local/bin/agg + - name: record + render demos + run: scripts/record-docs.sh --render + - uses: actions/upload-artifact@v4 + with: + name: doc-casts + path: | + docs/assets/casts/*.cast + docs/assets/casts/*.gif + docs/assets/casts/*.svg + if-no-files-found: warn diff --git a/docs/assets/casts/.gitignore b/docs/assets/casts/.gitignore new file mode 100644 index 0000000..79eaf9c --- /dev/null +++ b/docs/assets/casts/.gitignore @@ -0,0 +1,4 @@ +# Generated asciicast recordings live here (scripts/record-docs.sh). +# .cast files are committed; rendered .svg/.gif are build artifacts. +*.svg +*.gif diff --git a/docs/demos/README.md b/docs/demos/README.md new file mode 100644 index 0000000..88b352d --- /dev/null +++ b/docs/demos/README.md @@ -0,0 +1,73 @@ + + +# Documentation demos (asciicast automation) + +This directory documents the **recording** half of the docs flow. It pairs with +the doc-validation harness (`test/docs/`, PR #2): + +| Concern | Owner | What it guarantees | +|---------|-------|--------------------| +| The copy-paste snippets in `docs/` actually run against the real binary | `test/docs/` (bats) | correctness — snippets don't drift | +| A demo *shows* those same commands running | `scripts/record-docs.sh` (virtui) | currency — the GIF/SVG matches today's binary | + +Both drive the **real `failsafe` binary** from the **same commands**, so prose, +tests, and recordings stay in lock-step. + +## How it works + +`scripts/record-docs.sh` uses [virtui](https://github.com/honeybadge-labs/virtui) +("Playwright for the terminal") to drive `failsafe` through a real PTY and record +each session to [asciicast v2](https://docs.asciinema.org/manual/asciicast/v2/): + +1. Build `failsafe` into a throwaway bindir and put it on `PATH`. +2. Point `$HOME` at a sandbox so demos never touch your real `~/.config/failsafe` + (same isolation trick as the harness's `helpers.bash`). +3. Start the virtui daemon, then for each scenario spawn a recorded `bash` + session, type the documented command, and `--wait` for the expected screen + text before moving on (deterministic, no fixed `sleep`s). +4. Write `docs/assets/casts/.cast`. +5. Optionally render each `.cast` to an embeddable `.svg`/`.gif` (`--render`) — + GitHub markdown and MkDocs do **not** play asciicast inline. + +```bash +scripts/record-docs.sh # record .cast files +scripts/record-docs.sh --render # also render .svg (svg-term) or .gif (agg) +``` + +If `virtui` is not installed the script **skips cleanly** (exit 0) with an +install hint — it never fails a build just because the recorder is absent, the +same "missing tool ⇒ skip, never a vacuous pass" rule the harness uses. + +## Scenarios + +| Cast | Command | Documents | +|------|---------|-----------| +| `explain-block.cast` | `failsafe explain "kubectl … delete ns payments"` | tutorials/getting-started, README — the guard blocking a destructive command | +| `mode-toggle.cast` | `failsafe mode get / set rw / set ro` | reference/modes, getting-started step 4 | +| `audit.cast` | `failsafe audit` | how-to/repo-policy — the policy chain in force | + +Add a scenario by appending one `record_scenario` block in the script; keep the +command byte-identical to the doc snippet it illustrates. + +## What is **not** automated (honest split) + +virtui is headless/VT100, so it can only record the **CLI** surface. The +GUI-only surfaces — WezTerm toasts & `format-tab-title`, iTerm2's Python-runtime +keybinding — are not recordable here and remain **manual**, exactly the +STATIC / LIVE-MANUAL surfaces called out in `test/docs/REPORT.md`. This script +automates the CLI documentation flow, not the *full* flow. + +## Requirements + +| Tool | Purpose | Install | +|------|---------|---------| +| [virtui](https://github.com/honeybadge-labs/virtui) | record the sessions | `go install github.com/honeybadge-labs/virtui/cmd/virtui@latest` | +| [agg](https://github.com/asciinema/agg) | `.cast` → `.gif` (optional) | `brew install agg` | +| [svg-term-cli](https://github.com/marionebl/svg-term-cli) | `.cast` → `.svg` (optional) | `npm i -g svg-term-cli` | + +> **Status:** prototype. virtui is early-stage (v0.2.0). The recorder is wired as +> an **on-demand** tool (and a `workflow_dispatch`-only CI job), not a required +> gate, so an unstable upstream can never block `main`. diff --git a/scripts/record-docs.sh b/scripts/record-docs.sh new file mode 100755 index 0000000..0a12a49 --- /dev/null +++ b/scripts/record-docs.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +# Copyright 2026 Undermountain Coding Company +# SPDX-License-Identifier: Apache-2.0 +# +# record-docs.sh — drive the failsafe CLI under virtui and emit asciicast +# recordings for the documentation site. +# +# This is the *recording* half of the docs automation flow. It is a sibling to +# the doc-validation harness (test/docs/, PR #2): that harness proves the +# copy-paste snippets in docs/ actually run against the real binary; this script +# produces the demo recordings that *show* them running, from the same commands, +# so the two never drift. +# +# It is deliberately scoped to the HEADLESS CLI surface (explain / mode / audit / +# report). The GUI terminal-integration surfaces (WezTerm toasts & tab-title, +# iTerm2's Python keybinding) are NOT recordable headlessly and stay manual — +# see docs/demos/README.md, same honest split as test/docs/REPORT.md. +# +# Requirements (all optional — missing tools downgrade gracefully, never fail): +# - virtui https://github.com/honeybadge-labs/virtui (the recorder) +# - agg https://github.com/asciinema/agg (.cast -> .gif) [optional render] +# - svg-term npm i -g svg-term-cli (.cast -> .svg) [optional render] +# +# Usage: +# scripts/record-docs.sh # record all scenarios into docs/assets/casts/ +# scripts/record-docs.sh --render # also render .cast -> .svg/.gif if a renderer is present +# COLS=100 ROWS=24 scripts/record-docs.sh +set -euo pipefail + +# ── Resolve paths ──────────────────────────────────────────────────────────── +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +OUT_DIR="${OUT_DIR:-$REPO_ROOT/docs/assets/casts}" +COLS="${COLS:-92}" +ROWS="${ROWS:-22}" +RENDER=0 +[[ "${1:-}" == "--render" ]] && RENDER=1 + +mkdir -p "$OUT_DIR" + +log() { printf '\033[1;34m▸\033[0m %s\n' "$*" >&2; } +warn() { printf '\033[1;33m!\033[0m %s\n' "$*" >&2; } +skip() { printf '\033[1;33mSKIP\033[0m %s\n' "$*" >&2; exit 0; } + +# ── Preflight: virtui ──────────────────────────────────────────────────────── +command -v virtui >/dev/null 2>&1 || skip \ + "virtui not found — install from https://github.com/honeybadge-labs/virtui (go install …/cmd/virtui@latest). No recordings produced." + +# ── Build the binary under test into a throwaway bindir ────────────────────── +BINDIR="$(mktemp -d)" +log "building failsafe -> $BINDIR/failsafe" +( cd "$REPO_ROOT" && go build -o "$BINDIR/failsafe" ./cmd/failsafe ) +export PATH="$BINDIR:$PATH" + +# ── Sandbox HOME so demos never touch the real ~/.config/failsafe ──────────── +# failsafe resolves all of its state from $HOME (policy.rego, trusted-repos.yaml, +# decisions.jsonl, pane-mode files). A throwaway HOME keeps demos deterministic +# and side-effect free, mirroring helpers.bash in the doc-validation harness. +SANDBOX_HOME="$(mktemp -d)" +export HOME="$SANDBOX_HOME" +mkdir -p "$SANDBOX_HOME/project" +unset FAILSAFE_MODE FAILSAFE_LOG 2>/dev/null || true + +# ── virtui daemon lifecycle ────────────────────────────────────────────────── +DAEMON_STARTED=0 +cleanup() { + [[ "$DAEMON_STARTED" == 1 ]] && virtui daemon stop >/dev/null 2>&1 || true + rm -rf "$BINDIR" "$SANDBOX_HOME" +} +trap cleanup EXIT + +log "starting virtui daemon" +virtui daemon start >/dev/null 2>&1 && DAEMON_STARTED=1 || warn "daemon may already be running" + +# ── record_scenario NAME -- "cmd|wait-for-text" ["cmd|wait" …] ─────────────── +# Spawns a recorded bash session, normalises the prompt, runs each step typing +# the command and waiting for the given screen text, then exits to flush the cast. +record_scenario() { + local name="$1"; shift + [[ "$1" == "--" ]] && shift + local cast="$OUT_DIR/$name.cast" + log "recording $name -> $cast" + + local sid + sid="$(virtui run --record --record-path "$cast" --cols "$COLS" --rows "$ROWS" bash)" + + # Quiet, deterministic prompt; start from the sandbox project dir. + virtui exec "$sid" "export PS1='\$ '; cd \"$HOME/project\"; clear" --wait-stable >/dev/null + + local step cmd want + for step in "$@"; do + cmd="${step%%|*}" + want="${step#*|}" + if [[ "$want" == "$step" || -z "$want" ]]; then + virtui exec "$sid" "$cmd" --wait-stable >/dev/null + else + virtui exec "$sid" "$cmd" --wait "$want" >/dev/null + fi + done + + virtui exec "$sid" "exit" --wait-stable >/dev/null 2>&1 || true +} + +# ── Scenarios — the headless CLI demos ─────────────────────────────────────── +# Each scenario maps to a doc page. Keep the commands identical to the snippets +# the doc-validation harness checks, so the recording and the prose can't drift. + +# 1. The money shot: the guard blocking a destructive command (tutorials/getting-started, README). +record_scenario "explain-block" -- \ + "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"|Decision: BLOCK" + +# 2. The read/write mode switch (reference/modes, tutorials/getting-started step 4). +record_scenario "mode-toggle" -- \ + "failsafe mode get|read" \ + "failsafe mode set rw|read & write" \ + "failsafe mode get|read & write" \ + "failsafe mode set ro|read" + +# 3. The policy chain in force (how-to/repo-policy, reference). +record_scenario "audit" -- \ + "failsafe audit|block rule" + +log "done — casts in $OUT_DIR" +ls -1 "$OUT_DIR"/*.cast 2>/dev/null >&2 || true + +# ── Optional render: .cast -> embeddable .svg / .gif ───────────────────────── +# GitHub markdown and MkDocs do NOT play asciicast inline, so an embeddable +# artifact is required to actually surface a demo in the docs. +if [[ "$RENDER" == 1 ]]; then + if command -v svg-term >/dev/null 2>&1; then + for cast in "$OUT_DIR"/*.cast; do + log "render $(basename "$cast") -> svg" + svg-term --in "$cast" --out "${cast%.cast}.svg" --window --no-cursor + done + elif command -v agg >/dev/null 2>&1; then + for cast in "$OUT_DIR"/*.cast; do + log "render $(basename "$cast") -> gif" + agg "$cast" "${cast%.cast}.gif" + done + else + warn "--render requested but neither svg-term nor agg found; left .cast files only." + fi +fi From 4c0b28c3f03fbce95dbb006fdf72bb205ab106c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 22:29:02 +0000 Subject: [PATCH 2/5] docs: generate the demo recordings and embed them across the docs Runs the prototype end-to-end with virtui v0.2.0 installed and wires the output into the documentation: - docs/assets/casts/{explain-block,mode-toggle,audit}.{cast,svg}: real recordings of the shipped binary, rendered to embeddable SVG with svg-term. The recorder now parses virtui's `session_id:` from `run` output (no jq dependency), kills the session to flush each cast, and uses a fixed sandbox HOME so the printed cwd is readable and the committed SVGs diff only when behaviour changes. - Embedded the SVGs in: README hero, getting-started steps 3 & 4, how-to/explain-a-command, reference/modes, how-to/repo-policy. - .gitignore now keeps .cast (source) and .svg (embedded) committed, ignoring only heavy .gif renders. Verified: `mkdocs build --strict` is green and every embedded image path resolves in the built site; all three SVGs are well-formed and contain the expected on-screen text (BLOCK / read & write / block rules). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0147YNPKgm4m7DqA92DtqJqP --- README.md | 5 +++++ docs/assets/casts/.gitignore | 5 ++--- docs/assets/casts/audit.cast | 17 +++++++++++++++++ docs/assets/casts/audit.svg | 1 + docs/assets/casts/explain-block.cast | 15 +++++++++++++++ docs/assets/casts/explain-block.svg | 1 + docs/assets/casts/mode-toggle.cast | 27 +++++++++++++++++++++++++++ docs/assets/casts/mode-toggle.svg | 1 + docs/how-to/explain-a-command.md | 4 ++++ docs/how-to/repo-policy.md | 6 ++++++ docs/reference/modes.md | 6 ++++++ docs/tutorials/getting-started.md | 10 ++++++++++ scripts/record-docs.sh | 16 ++++++++++++---- 13 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 docs/assets/casts/audit.cast create mode 100644 docs/assets/casts/audit.svg create mode 100644 docs/assets/casts/explain-block.cast create mode 100644 docs/assets/casts/explain-block.svg create mode 100644 docs/assets/casts/mode-toggle.cast create mode 100644 docs/assets/casts/mode-toggle.svg diff --git a/README.md b/README.md index 039cdc8..43d63a1 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,11 @@ $ kubectl --context arn:aws:eks:us-east-1:…:cluster/prod delete ns payments flip this pane: failsafe toggle ``` +

+ failsafe explain blocking a kubectl delete on prod, recorded against the real binary +
↑ a real recording — failsafe explain against the shipped binary, regenerated by scripts/record-docs.sh +

+ The only thing between a hijacked or careless agent and a wiped cluster is you, clicking "allow" on the hundredth prompt — and that fails exactly when you're tired or moving fast. **failsafe is the guard that doesn't get tired.** diff --git a/docs/assets/casts/.gitignore b/docs/assets/casts/.gitignore index 79eaf9c..098d636 100644 --- a/docs/assets/casts/.gitignore +++ b/docs/assets/casts/.gitignore @@ -1,4 +1,3 @@ -# Generated asciicast recordings live here (scripts/record-docs.sh). -# .cast files are committed; rendered .svg/.gif are build artifacts. -*.svg +# Rendered GIFs are heavy build artifacts — regenerate with scripts/record-docs.sh --render. +# .cast (source) and .svg (embedded in docs) ARE committed. *.gif diff --git a/docs/assets/casts/audit.cast b/docs/assets/casts/audit.cast new file mode 100644 index 0000000..c43a0bb --- /dev/null +++ b/docs/assets/casts/audit.cast @@ -0,0 +1,17 @@ +{"version":2,"width":92,"height":22,"timestamp":1782339943} +[0.003755, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.008253, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.008525, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear"] +[0.008546, "o", "\r\n\u001b[?2004l\r"] +[0.010017, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.010129, "o", "\u001b[?2004h$ "] +[0.519723, "i", "failsafe audit\r"] +[0.519915, "o", "failsafe audit\r\n\u001b[?2004l\r"] +[0.525795, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n bundled/aws.rego — 2 block rule(s)\r\n bundled/failsafe.rego — 2 block rule(s)\r\n bundled/git.rego — 0 block rule(s)\r\n"] +[0.525864, "o", " bundled/helm.rego — 2 block rule(s)\r\n"] +[0.526258, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] +[0.526557, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override.\r\n"] +[0.529228, "o", "\u001b[?2004h$ "] +[0.536532, "i", "exit\r"] +[0.536679, "o", "exit\r\n\u001b[?2004l\r"] +[0.536790, "o", "exit\r\n"] diff --git a/docs/assets/casts/audit.svg b/docs/assets/casts/audit.svg new file mode 100644 index 0000000..3b63712 --- /dev/null +++ b/docs/assets/casts/audit.svg @@ -0,0 +1 @@ +root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file diff --git a/docs/assets/casts/explain-block.cast b/docs/assets/casts/explain-block.cast new file mode 100644 index 0000000..0275b70 --- /dev/null +++ b/docs/assets/casts/explain-block.cast @@ -0,0 +1,15 @@ +{"version":2,"width":92,"height":22,"timestamp":1782339940} +[0.004430, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.009964, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.010413, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] +[0.011918, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.011999, "o", "\u001b[?2004h$ "] +[0.521587, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] +[0.522116, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\"\r\n\u001b[?2004l\r"] +[0.536497, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] +[0.538120, "o", "\u001b[?2004h"] +[0.538170, "o", "$ "] +[0.545187, "i", "exit\r"] +[0.545319, "o", "exit"] +[0.545338, "o", "\r\n\u001b[?2004l\r"] +[0.545383, "o", "exit\r\n"] diff --git a/docs/assets/casts/explain-block.svg b/docs/assets/casts/explain-block.svg new file mode 100644 index 0000000..41f4c07 --- /dev/null +++ b/docs/assets/casts/explain-block.svg @@ -0,0 +1 @@ +$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file diff --git a/docs/assets/casts/mode-toggle.cast b/docs/assets/casts/mode-toggle.cast new file mode 100644 index 0000000..ee58b07 --- /dev/null +++ b/docs/assets/casts/mode-toggle.cast @@ -0,0 +1,27 @@ +{"version":2,"width":92,"height":22,"timestamp":1782339941} +[0.003908, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.007958, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.008188, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n"] +[0.008212, "o", "\u001b[?2004l\r"] +[0.009575, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.009692, "o", "\u001b[?2004h$ "] +[0.519575, "i", "failsafe mode get\r"] +[0.519753, "o", "failsafe mode get\r\n\u001b[?2004l\r"] +[0.524781, "o", "read\t(default; no source resolved)\r\n"] +[0.527223, "o", "\u001b[?2004h$ "] +[0.533467, "i", "failsafe mode set rw\r"] +[0.533763, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] +[0.538670, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[0.540489, "o", "\u001b[?2004h$ "] +[0.546741, "i", "failsafe mode get\r"] +[0.547383, "o", "failsafe mode get\r\n\u001b[?2004l\r"] +[0.551718, "o", "read \u0026 write\t(env)\r\n"] +[0.552561, "o", "\u001b[?2004h$ "] +[0.554560, "i", "failsafe mode set ro\r"] +[0.554794, "o", "failsafe mode set ro\r\n"] +[0.555137, "o", "\u001b[?2004l\r"] +[0.559703, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[0.560457, "o", "\u001b[?2004h$ "] +[0.563176, "i", "exit\r"] +[0.563396, "o", "exit\r\n\u001b[?2004l\r"] +[0.563446, "o", "exit\r\n"] diff --git a/docs/assets/casts/mode-toggle.svg b/docs/assets/casts/mode-toggle.svg new file mode 100644 index 0000000..d06fe79 --- /dev/null +++ b/docs/assets/casts/mode-toggle.svg @@ -0,0 +1 @@ +root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file diff --git a/docs/how-to/explain-a-command.md b/docs/how-to/explain-a-command.md index 204843c..8f362e9 100644 --- a/docs/how-to/explain-a-command.md +++ b/docs/how-to/explain-a-command.md @@ -21,6 +21,10 @@ failsafe explain kubectl delete ns payments Both forms are equivalent: the command is re-assembled and fed through the same shell parser the hook uses. +![failsafe explain blocking a destructive command](../assets/casts/explain-block.svg) + +*Recording regenerated by [`scripts/record-docs.sh`](https://github.com/UndermountainCC/failsafe/blob/main/scripts/record-docs.sh).* + ## Reading the output ``` diff --git a/docs/how-to/repo-policy.md b/docs/how-to/repo-policy.md index d309e55..5f4de1e 100644 --- a/docs/how-to/repo-policy.md +++ b/docs/how-to/repo-policy.md @@ -109,6 +109,12 @@ Decision: BLOCK Reason : this repo: kubectl apply via CI only — submit a PR ``` +To see every layer that applies at a path at once — not just one command's verdict — run `failsafe audit`: + +![failsafe audit listing the policy chain in force](../assets/casts/audit.svg) + +*Recording regenerated by [`scripts/record-docs.sh`](https://github.com/UndermountainCC/failsafe/blob/main/scripts/record-docs.sh).* + ## 5. Commit and review with teammates `.failsafe.rego` is a first-class source file. Commit it, review it in PRs, and keep it next to the infra code it governs. diff --git a/docs/reference/modes.md b/docs/reference/modes.md index fd97917..de33ab1 100644 --- a/docs/reference/modes.md +++ b/docs/reference/modes.md @@ -18,6 +18,12 @@ The two mode values and the exact source-resolution chain that determines which The `allow_override` mechanism is separate from mode: a repo policy can override a block at any mode. See the [bundled policies](bundled-policies.md) and [configuration](configuration.md) pages. +Flipping the current pane between the two values with `mode get` / `mode set`: + +![failsafe mode get, set rw, set ro](../assets/casts/mode-toggle.svg) + +*Recording regenerated by [`scripts/record-docs.sh`](https://github.com/UndermountainCC/failsafe/blob/main/scripts/record-docs.sh).* + --- ## Mode source chain diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md index a6f3822..633de8e 100644 --- a/docs/tutorials/getting-started.md +++ b/docs/tutorials/getting-started.md @@ -95,6 +95,12 @@ Reason : kubectl delete blocked while failsafe is enabled !!! success "You just verified the guard" failsafe parsed the command, identified the tool (`kubectl`), extracted the verb (`delete`), and blocked it before any agent or cluster was involved. +The same run, recorded against the real binary: + +![failsafe explain blocking kubectl delete on prod](../assets/casts/explain-block.svg) + +*Recording regenerated by [`scripts/record-docs.sh`](https://github.com/UndermountainCC/failsafe/blob/main/scripts/record-docs.sh) — see [docs/demos](https://github.com/UndermountainCC/failsafe/tree/main/docs/demos).* + --- ## Step 4: Flip to write mode and back @@ -115,6 +121,10 @@ failsafe mode get disabled ``` +Flipping to write and back, recorded: + +![failsafe mode get, set rw, set ro](../assets/casts/mode-toggle.svg) + Now run a read-only `kubectl get` to confirm allowed commands still pass through: ```bash diff --git a/scripts/record-docs.sh b/scripts/record-docs.sh index 0a12a49..6b2b8dc 100755 --- a/scripts/record-docs.sh +++ b/scripts/record-docs.sh @@ -54,9 +54,12 @@ export PATH="$BINDIR:$PATH" # ── Sandbox HOME so demos never touch the real ~/.config/failsafe ──────────── # failsafe resolves all of its state from $HOME (policy.rego, trusted-repos.yaml, -# decisions.jsonl, pane-mode files). A throwaway HOME keeps demos deterministic -# and side-effect free, mirroring helpers.bash in the doc-validation harness. -SANDBOX_HOME="$(mktemp -d)" +# decisions.jsonl, pane-mode files). A throwaway HOME keeps demos side-effect +# free, mirroring helpers.bash in the doc-validation harness. The path is FIXED +# (not mktemp-random) so the cwd printed by `explain` is readable and identical +# on every re-record — committed SVGs then diff only when behaviour changes. +SANDBOX_HOME="${TMPDIR:-/tmp}/failsafe-docs-home" +rm -rf "$SANDBOX_HOME" export HOME="$SANDBOX_HOME" mkdir -p "$SANDBOX_HOME/project" unset FAILSAFE_MODE FAILSAFE_LOG 2>/dev/null || true @@ -81,8 +84,12 @@ record_scenario() { local cast="$OUT_DIR/$name.cast" log "recording $name -> $cast" + # `virtui run` prints a human block ("session_id: \npid: …"); pull the id + # out of it so we don't take a hard dependency on jq. local sid - sid="$(virtui run --record --record-path "$cast" --cols "$COLS" --rows "$ROWS" bash)" + sid="$(virtui run --record --record-path "$cast" --cols "$COLS" --rows "$ROWS" bash \ + | sed -n 's/^session_id: //p')" + [[ -n "$sid" ]] || { warn "could not start session for $name"; return 1; } # Quiet, deterministic prompt; start from the sandbox project dir. virtui exec "$sid" "export PS1='\$ '; cd \"$HOME/project\"; clear" --wait-stable >/dev/null @@ -99,6 +106,7 @@ record_scenario() { done virtui exec "$sid" "exit" --wait-stable >/dev/null 2>&1 || true + virtui kill "$sid" >/dev/null 2>&1 || true # finalize/flush the recording } # ── Scenarios — the headless CLI demos ─────────────────────────────────────── From 5e55fbbb272e67904cdc90b7fc122a296ad0d36b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 02:28:23 +0000 Subject: [PATCH 3/5] =?UTF-8?q?docs:=20pace=20the=20demo=20recordings=20?= =?UTF-8?q?=E2=80=94=20~2s=20pause=20on=20every=20frame?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The recordings played as a blur. Add a configurable STEP_PAUSE (default 2s) held before each command and on the final frame. virtui records in real wall-clock time, so a host-side idle gap becomes a visible pause in the cast; svg-term preserves the full timeline (mode-toggle now animates over 10.6s instead of <1s). Set STEP_PAUSE=0 for a fast smoke-test recording. Regenerated all three casts + SVGs at the new pace. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0147YNPKgm4m7DqA92DtqJqP --- docs/assets/casts/audit.cast | 37 ++++++++++--------- docs/assets/casts/audit.svg | 2 +- docs/assets/casts/explain-block.cast | 32 +++++++++-------- docs/assets/casts/explain-block.svg | 2 +- docs/assets/casts/mode-toggle.cast | 53 ++++++++++++++-------------- docs/assets/casts/mode-toggle.svg | 2 +- scripts/record-docs.sh | 6 ++++ 7 files changed, 72 insertions(+), 62 deletions(-) diff --git a/docs/assets/casts/audit.cast b/docs/assets/casts/audit.cast index c43a0bb..723a1bc 100644 --- a/docs/assets/casts/audit.cast +++ b/docs/assets/casts/audit.cast @@ -1,17 +1,20 @@ -{"version":2,"width":92,"height":22,"timestamp":1782339943} -[0.003755, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.008253, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.008525, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear"] -[0.008546, "o", "\r\n\u001b[?2004l\r"] -[0.010017, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.010129, "o", "\u001b[?2004h$ "] -[0.519723, "i", "failsafe audit\r"] -[0.519915, "o", "failsafe audit\r\n\u001b[?2004l\r"] -[0.525795, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n bundled/aws.rego — 2 block rule(s)\r\n bundled/failsafe.rego — 2 block rule(s)\r\n bundled/git.rego — 0 block rule(s)\r\n"] -[0.525864, "o", " bundled/helm.rego — 2 block rule(s)\r\n"] -[0.526258, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] -[0.526557, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override.\r\n"] -[0.529228, "o", "\u001b[?2004h$ "] -[0.536532, "i", "exit\r"] -[0.536679, "o", "exit\r\n\u001b[?2004l\r"] -[0.536790, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354475} +[0.004951, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.008205, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.008399, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear"] +[0.008420, "o", "\r\n\u001b[?2004l\r"] +[0.010564, "o", "\u001b[H\u001b[2J\u001b[3J\u001b[?2004h$ "] +[2.522163, "i", "failsafe audit\r"] +[2.522478, "o", "failsafe audit\r\n"] +[2.522511, "o", "\u001b[?2004l\r"] +[2.527974, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n"] +[2.528171, "o", " bundled/aws.rego — 2 block rule(s)\r\n"] +[2.528400, "o", " bundled/failsafe.rego — 2 block rule(s)\r\n"] +[2.528431, "o", " bundled/git.rego — 0 block rule(s)\r\n"] +[2.528646, "o", " bundled/helm.rego — 2 block rule(s)\r\n"] +[2.528791, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] +[2.528934, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override.\r\n"] +[2.529863, "o", "\u001b[?2004h$ "] +[4.538371, "i", "exit\r"] +[4.538576, "o", "exit\r\n\u001b[?2004l\r"] +[4.538663, "o", "exit\r\n"] diff --git a/docs/assets/casts/audit.svg b/docs/assets/casts/audit.svg index 3b63712..8a4cbb0 100644 --- a/docs/assets/casts/audit.svg +++ b/docs/assets/casts/audit.svg @@ -1 +1 @@ -root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file +root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file diff --git a/docs/assets/casts/explain-block.cast b/docs/assets/casts/explain-block.cast index 0275b70..21d939d 100644 --- a/docs/assets/casts/explain-block.cast +++ b/docs/assets/casts/explain-block.cast @@ -1,15 +1,17 @@ -{"version":2,"width":92,"height":22,"timestamp":1782339940} -[0.004430, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.009964, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.010413, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] -[0.011918, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.011999, "o", "\u001b[?2004h$ "] -[0.521587, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] -[0.522116, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\"\r\n\u001b[?2004l\r"] -[0.536497, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] -[0.538120, "o", "\u001b[?2004h"] -[0.538170, "o", "$ "] -[0.545187, "i", "exit\r"] -[0.545319, "o", "exit"] -[0.545338, "o", "\r\n\u001b[?2004l\r"] -[0.545383, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354459} +[0.014111, "o", "\u001b[?2004h"] +[0.014259, "o", "root@vm:/home/user/failsafe# "] +[0.015639, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.016052, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n"] +[0.016085, "o", "\u001b[?2004l\r"] +[0.018898, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.019013, "o", "\u001b[?2004h$ "] +[2.535743, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] +[2.536062, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\""] +[2.536096, "o", "\r\n\u001b[?2004l\r"] +[2.552120, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego"] +[2.552259, "o", "\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] +[2.553503, "o", "\u001b[?2004h$ "] +[4.562749, "i", "exit\r"] +[4.562943, "o", "exit\r\n\u001b[?2004l\r"] +[4.563005, "o", "exit\r\n"] diff --git a/docs/assets/casts/explain-block.svg b/docs/assets/casts/explain-block.svg index 41f4c07..40a8cae 100644 --- a/docs/assets/casts/explain-block.svg +++ b/docs/assets/casts/explain-block.svg @@ -1 +1 @@ -$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file +root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file diff --git a/docs/assets/casts/mode-toggle.cast b/docs/assets/casts/mode-toggle.cast index ee58b07..aa0fe6c 100644 --- a/docs/assets/casts/mode-toggle.cast +++ b/docs/assets/casts/mode-toggle.cast @@ -1,27 +1,26 @@ -{"version":2,"width":92,"height":22,"timestamp":1782339941} -[0.003908, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.007958, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.008188, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n"] -[0.008212, "o", "\u001b[?2004l\r"] -[0.009575, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.009692, "o", "\u001b[?2004h$ "] -[0.519575, "i", "failsafe mode get\r"] -[0.519753, "o", "failsafe mode get\r\n\u001b[?2004l\r"] -[0.524781, "o", "read\t(default; no source resolved)\r\n"] -[0.527223, "o", "\u001b[?2004h$ "] -[0.533467, "i", "failsafe mode set rw\r"] -[0.533763, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] -[0.538670, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[0.540489, "o", "\u001b[?2004h$ "] -[0.546741, "i", "failsafe mode get\r"] -[0.547383, "o", "failsafe mode get\r\n\u001b[?2004l\r"] -[0.551718, "o", "read \u0026 write\t(env)\r\n"] -[0.552561, "o", "\u001b[?2004h$ "] -[0.554560, "i", "failsafe mode set ro\r"] -[0.554794, "o", "failsafe mode set ro\r\n"] -[0.555137, "o", "\u001b[?2004l\r"] -[0.559703, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[0.560457, "o", "\u001b[?2004h$ "] -[0.563176, "i", "exit\r"] -[0.563396, "o", "exit\r\n\u001b[?2004l\r"] -[0.563446, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354464} +[0.003562, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.007894, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.008235, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] +[0.009608, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.009648, "o", "\u001b[?2004h$ "] +[2.520649, "i", "failsafe mode get\r"] +[2.520904, "o", "failsafe mode get"] +[2.520952, "o", "\r\n\u001b[?2004l\r"] +[2.525788, "o", "read\t(default; no source resolved)\r\n"] +[2.527069, "o", "\u001b[?2004h$ "] +[4.535621, "i", "failsafe mode set rw\r"] +[4.536304, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] +[4.540645, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[4.541648, "o", "\u001b[?2004h$ "] +[6.552378, "i", "failsafe mode get\r"] +[6.552618, "o", "failsafe mode get"] +[6.552706, "o", "\r\n\u001b[?2004l\r"] +[6.557972, "o", "read \u0026 write\t(env)\r\n"] +[6.558959, "o", "\u001b[?2004h$ "] +[8.562204, "i", "failsafe mode set ro\r"] +[8.562428, "o", "failsafe mode set ro\r\n\u001b[?2004l\r"] +[8.567532, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[8.568727, "o", "\u001b[?2004h$ "] +[10.572343, "i", "exit\r"] +[10.572618, "o", "exit\r\n\u001b[?2004l\rexit\r\n"] diff --git a/docs/assets/casts/mode-toggle.svg b/docs/assets/casts/mode-toggle.svg index d06fe79..0870174 100644 --- a/docs/assets/casts/mode-toggle.svg +++ b/docs/assets/casts/mode-toggle.svg @@ -1 +1 @@ -root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file +$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)root@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$exitexit \ No newline at end of file diff --git a/scripts/record-docs.sh b/scripts/record-docs.sh index 6b2b8dc..36c7aed 100755 --- a/scripts/record-docs.sh +++ b/scripts/record-docs.sh @@ -33,6 +33,10 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" OUT_DIR="${OUT_DIR:-$REPO_ROOT/docs/assets/casts}" COLS="${COLS:-92}" ROWS="${ROWS:-22}" +# Pause (seconds) held on each frame so playback is readable, not a blur. virtui +# records in real wall-clock time, so an idle gap on the host becomes a visible +# pause in the cast. Override with STEP_PAUSE=0 for a fast smoke-test recording. +STEP_PAUSE="${STEP_PAUSE:-2}" RENDER=0 [[ "${1:-}" == "--render" ]] && RENDER=1 @@ -98,6 +102,7 @@ record_scenario() { for step in "$@"; do cmd="${step%%|*}" want="${step#*|}" + sleep "$STEP_PAUSE" # hold the previous frame so commands don't blur together if [[ "$want" == "$step" || -z "$want" ]]; then virtui exec "$sid" "$cmd" --wait-stable >/dev/null else @@ -105,6 +110,7 @@ record_scenario() { fi done + sleep "$STEP_PAUSE" # let the final output linger before the session closes virtui exec "$sid" "exit" --wait-stable >/dev/null 2>&1 || true virtui kill "$sid" >/dev/null 2>&1 || true # finalize/flush the recording } From 3b6469a031fe587eebb16aa6cd277e318d3cd2c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 02:35:11 +0000 Subject: [PATCH 4/5] docs: pause AFTER each command, not before MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the STEP_PAUSE linger to after each command's output instead of before the next one — playback now rests on each result (the BLOCK verdict, the flipped mode) before moving on, with no dead air on an empty prompt first. Timing is baked into the cast, so this is a re-record, not a re-render. Regenerated all three casts + SVGs. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_0147YNPKgm4m7DqA92DtqJqP --- docs/assets/casts/audit.cast | 41 ++++++++++----------- docs/assets/casts/audit.svg | 2 +- docs/assets/casts/explain-block.cast | 31 ++++++++-------- docs/assets/casts/explain-block.svg | 2 +- docs/assets/casts/mode-toggle.cast | 54 ++++++++++++++-------------- docs/assets/casts/mode-toggle.svg | 2 +- scripts/record-docs.sh | 10 +++--- 7 files changed, 71 insertions(+), 71 deletions(-) diff --git a/docs/assets/casts/audit.cast b/docs/assets/casts/audit.cast index 723a1bc..ce17b1a 100644 --- a/docs/assets/casts/audit.cast +++ b/docs/assets/casts/audit.cast @@ -1,20 +1,21 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354475} -[0.004951, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.008205, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.008399, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear"] -[0.008420, "o", "\r\n\u001b[?2004l\r"] -[0.010564, "o", "\u001b[H\u001b[2J\u001b[3J\u001b[?2004h$ "] -[2.522163, "i", "failsafe audit\r"] -[2.522478, "o", "failsafe audit\r\n"] -[2.522511, "o", "\u001b[?2004l\r"] -[2.527974, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n"] -[2.528171, "o", " bundled/aws.rego — 2 block rule(s)\r\n"] -[2.528400, "o", " bundled/failsafe.rego — 2 block rule(s)\r\n"] -[2.528431, "o", " bundled/git.rego — 0 block rule(s)\r\n"] -[2.528646, "o", " bundled/helm.rego — 2 block rule(s)\r\n"] -[2.528791, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] -[2.528934, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override.\r\n"] -[2.529863, "o", "\u001b[?2004h$ "] -[4.538371, "i", "exit\r"] -[4.538576, "o", "exit\r\n\u001b[?2004l\r"] -[4.538663, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354894} +[0.004957, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.009090, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.009370, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] +[0.010662, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.010778, "o", "\u001b[?2004h$ "] +[0.522810, "i", "failsafe audit\r"] +[0.523095, "o", "failsafe audit\r\n"] +[0.523149, "o", "\u001b[?2004l\r"] +[0.528713, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n"] +[0.528859, "o", " bundled/aws.rego — 2 block rule(s)\r\n"] +[0.528966, "o", " bundled/failsafe.rego — 2 block rule(s)\r\n"] +[0.529186, "o", " bundled/git.rego — 0 block rule(s)\r\n bundled/helm.rego — 2 block rule(s)\r\n"] +[0.529427, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] +[0.529509, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override."] +[0.529577, "o", "\r\n"] +[0.530404, "o", "\u001b[?2004h$ "] +[2.539712, "i", "exit\r"] +[2.539852, "o", "exit"] +[2.539889, "o", "\r\n\u001b[?2004l\r"] +[2.539922, "o", "exit\r\n"] diff --git a/docs/assets/casts/audit.svg b/docs/assets/casts/audit.svg index 8a4cbb0..5176f5b 100644 --- a/docs/assets/casts/audit.svg +++ b/docs/assets/casts/audit.svg @@ -1 +1 @@ -root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file +$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file diff --git a/docs/assets/casts/explain-block.cast b/docs/assets/casts/explain-block.cast index 21d939d..3811329 100644 --- a/docs/assets/casts/explain-block.cast +++ b/docs/assets/casts/explain-block.cast @@ -1,17 +1,14 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354459} -[0.014111, "o", "\u001b[?2004h"] -[0.014259, "o", "root@vm:/home/user/failsafe# "] -[0.015639, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.016052, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n"] -[0.016085, "o", "\u001b[?2004l\r"] -[0.018898, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.019013, "o", "\u001b[?2004h$ "] -[2.535743, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] -[2.536062, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\""] -[2.536096, "o", "\r\n\u001b[?2004l\r"] -[2.552120, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego"] -[2.552259, "o", "\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] -[2.553503, "o", "\u001b[?2004h$ "] -[4.562749, "i", "exit\r"] -[4.562943, "o", "exit\r\n\u001b[?2004l\r"] -[4.563005, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354881} +[0.010649, "o", "\u001b[?2004h"] +[0.010887, "o", "root@vm:/home/user/failsafe# "] +[0.011159, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.012521, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] +[0.014478, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.014559, "o", "\u001b[?2004h$ "] +[0.524948, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] +[0.525498, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\"\r\n\u001b[?2004l\r"] +[0.546886, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] +[0.548369, "o", "\u001b[?2004h$ "] +[2.559258, "i", "exit\r"] +[2.559434, "o", "exit\r\n\u001b[?2004l\r"] +[2.559469, "o", "exit\r\n"] diff --git a/docs/assets/casts/explain-block.svg b/docs/assets/casts/explain-block.svg index 40a8cae..d87fa2b 100644 --- a/docs/assets/casts/explain-block.svg +++ b/docs/assets/casts/explain-block.svg @@ -1 +1 @@ -root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#exit \ No newline at end of file +$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file diff --git a/docs/assets/casts/mode-toggle.cast b/docs/assets/casts/mode-toggle.cast index aa0fe6c..84a9839 100644 --- a/docs/assets/casts/mode-toggle.cast +++ b/docs/assets/casts/mode-toggle.cast @@ -1,26 +1,28 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354464} -[0.003562, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.007894, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.008235, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] -[0.009608, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.009648, "o", "\u001b[?2004h$ "] -[2.520649, "i", "failsafe mode get\r"] -[2.520904, "o", "failsafe mode get"] -[2.520952, "o", "\r\n\u001b[?2004l\r"] -[2.525788, "o", "read\t(default; no source resolved)\r\n"] -[2.527069, "o", "\u001b[?2004h$ "] -[4.535621, "i", "failsafe mode set rw\r"] -[4.536304, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] -[4.540645, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[4.541648, "o", "\u001b[?2004h$ "] -[6.552378, "i", "failsafe mode get\r"] -[6.552618, "o", "failsafe mode get"] -[6.552706, "o", "\r\n\u001b[?2004l\r"] -[6.557972, "o", "read \u0026 write\t(env)\r\n"] -[6.558959, "o", "\u001b[?2004h$ "] -[8.562204, "i", "failsafe mode set ro\r"] -[8.562428, "o", "failsafe mode set ro\r\n\u001b[?2004l\r"] -[8.567532, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[8.568727, "o", "\u001b[?2004h$ "] -[10.572343, "i", "exit\r"] -[10.572618, "o", "exit\r\n\u001b[?2004l\rexit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1782354884} +[0.003421, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] +[0.011223, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] +[0.011447, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] +[0.012870, "o", "\u001b[H\u001b[2J\u001b[3J"] +[0.012987, "o", "\u001b[?2004h$ "] +[0.524665, "i", "failsafe mode get\r"] +[0.524865, "o", "failsafe mode get\r\n"] +[0.524894, "o", "\u001b[?2004l\r"] +[0.529962, "o", "read\t(default; no source resolved)\r\n"] +[0.530698, "o", "\u001b[?2004h"] +[0.530812, "o", "$ "] +[2.547466, "i", "failsafe mode set rw\r"] +[2.547696, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] +[2.553079, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[2.553864, "o", "\u001b[?2004h$ "] +[4.563007, "i", "failsafe mode get\r"] +[4.563219, "o", "failsafe mode get\r\n\u001b[?2004l\r"] +[4.568191, "o", "read \u0026 write\t(env)\r\n"] +[4.569172, "o", "\u001b[?2004h$ "] +[6.576488, "i", "failsafe mode set ro\r"] +[6.576756, "o", "failsafe mode set ro"] +[6.576771, "o", "\r\n\u001b[?2004l\r"] +[6.582397, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] +[6.583490, "o", "\u001b[?2004h$ "] +[8.586430, "i", "exit\r"] +[8.586669, "o", "exit\r\n\u001b[?2004l\r"] +[8.586724, "o", "exit\r\n"] diff --git a/docs/assets/casts/mode-toggle.svg b/docs/assets/casts/mode-toggle.svg index 0870174..ea0338c 100644 --- a/docs/assets/casts/mode-toggle.svg +++ b/docs/assets/casts/mode-toggle.svg @@ -1 +1 @@ -$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)root@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clear$exitexit \ No newline at end of file +$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file diff --git a/scripts/record-docs.sh b/scripts/record-docs.sh index 36c7aed..98c430b 100755 --- a/scripts/record-docs.sh +++ b/scripts/record-docs.sh @@ -33,9 +33,10 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" OUT_DIR="${OUT_DIR:-$REPO_ROOT/docs/assets/casts}" COLS="${COLS:-92}" ROWS="${ROWS:-22}" -# Pause (seconds) held on each frame so playback is readable, not a blur. virtui -# records in real wall-clock time, so an idle gap on the host becomes a visible -# pause in the cast. Override with STEP_PAUSE=0 for a fast smoke-test recording. +# Pause (seconds) held AFTER each command's output, so playback lingers on the +# result before moving on instead of blurring past it. virtui records in real +# wall-clock time, so a host-side idle gap becomes a visible pause baked into the +# cast (re-rendering can't add it — it must be recorded). STEP_PAUSE=0 = fast. STEP_PAUSE="${STEP_PAUSE:-2}" RENDER=0 [[ "${1:-}" == "--render" ]] && RENDER=1 @@ -102,15 +103,14 @@ record_scenario() { for step in "$@"; do cmd="${step%%|*}" want="${step#*|}" - sleep "$STEP_PAUSE" # hold the previous frame so commands don't blur together if [[ "$want" == "$step" || -z "$want" ]]; then virtui exec "$sid" "$cmd" --wait-stable >/dev/null else virtui exec "$sid" "$cmd" --wait "$want" >/dev/null fi + sleep "$STEP_PAUSE" # linger on this command's output before the next one done - sleep "$STEP_PAUSE" # let the final output linger before the session closes virtui exec "$sid" "exit" --wait-stable >/dev/null 2>&1 || true virtui kill "$sid" >/dev/null 2>&1 || true # finalize/flush the recording } From 5b01197d8ae4adb3d76cb5204647f3868b395e8f Mon Sep 17 00:00:00 2001 From: Tombar Date: Fri, 17 Jul 2026 20:45:51 -0300 Subject: [PATCH 5/5] =?UTF-8?q?docs(demos):=20rebase=20onto=20enabled/disa?= =?UTF-8?q?bled=20rename=20=E2=80=94=20re-record=20casts=20against=20curre?= =?UTF-8?q?nt=20binary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - mode-toggle scenario now records the documented commands (mode set disabled / enabled); later steps use --wait-stable since their wait words already sit on screen from earlier steps - captions/alt text updated to enabled/disabled vocabulary - drop stale 'PR #2' references (harness landed on main) - all three casts + SVGs regenerated against the post-rename binary Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01TiYGMkQeeiUQ6vWVqJaaQc --- docs/assets/casts/audit.cast | 50 ++++++++++++-------- docs/assets/casts/audit.svg | 2 +- docs/assets/casts/explain-block.cast | 47 ++++++++++++------ docs/assets/casts/explain-block.svg | 2 +- docs/assets/casts/mode-toggle.cast | 71 +++++++++++++++++----------- docs/assets/casts/mode-toggle.svg | 2 +- docs/demos/README.md | 4 +- docs/reference/modes.md | 2 +- docs/tutorials/getting-started.md | 4 +- scripts/record-docs.sh | 14 +++--- 10 files changed, 121 insertions(+), 77 deletions(-) diff --git a/docs/assets/casts/audit.cast b/docs/assets/casts/audit.cast index ce17b1a..e0c871b 100644 --- a/docs/assets/casts/audit.cast +++ b/docs/assets/casts/audit.cast @@ -1,21 +1,29 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354894} -[0.004957, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.009090, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.009370, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] -[0.010662, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.010778, "o", "\u001b[?2004h$ "] -[0.522810, "i", "failsafe audit\r"] -[0.523095, "o", "failsafe audit\r\n"] -[0.523149, "o", "\u001b[?2004l\r"] -[0.528713, "o", "Policy layers in effect at /tmp/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n"] -[0.528859, "o", " bundled/aws.rego — 2 block rule(s)\r\n"] -[0.528966, "o", " bundled/failsafe.rego — 2 block rule(s)\r\n"] -[0.529186, "o", " bundled/git.rego — 0 block rule(s)\r\n bundled/helm.rego — 2 block rule(s)\r\n"] -[0.529427, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] -[0.529509, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override."] -[0.529577, "o", "\r\n"] -[0.530404, "o", "\u001b[?2004h$ "] -[2.539712, "i", "exit\r"] -[2.539852, "o", "exit"] -[2.539889, "o", "\r\n\u001b[?2004l\r"] -[2.539922, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1784331898} +[0.005935, "o", "\r\nThe default interactive shell is now zsh.\r\nTo update your account to use zsh, please run `chsh -s /bin/zsh`.\r\nFor more details, please visit https://support.apple.com/kb/HT208050.\r\n"] +[0.006175, "o", "\u001b[?1034h"] +[0.006191, "o", "bash-3.2$ "] +[0.013003, "i", "export PS1='$ '; cd \"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/project\"; clear\r"] +[0.013056, "o", "e"] +[0.013092, "o", "xport PS1='$ '; "] +[0.013099, "o", "cd"] +[0.013206, "o", " \"/var/folders/12/mydd4m455zn9t"] +[0.013214, "o", "__"] +[0.013315, "o", "48syck4940000gn/T//fa"] +[0.013368, "o", "ilsafe-do \rcs-"] +[0.013432, "o", "home/project\"; clear\r\n"] +[0.015773, "o", "\u001b[3J\u001b[H\u001b[2J"] +[0.016056, "o", "$ "] +[0.543033, "i", "failsafe audit\r"] +[0.543163, "o", "f"] +[0.543232, "o", "ailsafe audit\r\n"] +[0.557055, "o", "Policy layers in effect at /var/folders/12/mydd4m455zn9t__48syck4940000gn/T/failsafe-docs-home/project:\r\n\r\n [bundled]\r\n"] +[0.557309, "o", " bundled/aws.rego — 2 block rule(s)\r\n"] +[0.557386, "o", " bundled/failsafe.rego — 2 block rule(s)\r\n"] +[0.557536, "o", " bundled/git.rego — 0 block rule(s)\r\n"] +[0.557630, "o", " bundled/helm.rego — 2 block rule(s)\r\n"] +[0.557722, "o", " bundled/kubectl.rego — 1 block rule(s)\r\n"] +[0.557803, "o", " bundled/terraform.rego — 2 block rule(s)\r\n\r\n9 block rules total, 0 allow_override.\r\n"] +[0.558949, "o", "$ "] +[2.585943, "i", "exit\r"] +[2.586019, "o", "ex"] +[2.586093, "o", "it\r\nexit\r\n"] diff --git a/docs/assets/casts/audit.svg b/docs/assets/casts/audit.svg index 5176f5b..9455ff9 100644 --- a/docs/assets/casts/audit.svg +++ b/docs/assets/casts/audit.svg @@ -1 +1 @@ -$$failsafeauditPolicylayersineffectat/tmp/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file +Thedefaultinteractiveshellisnowzsh.Toupdateyouraccounttousezsh,pleaserun`chsh-s/bin/zsh`.Formoredetails,pleasevisithttps://support.apple.com/kb/HT208050.bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-do$$failsafeauditPolicylayersineffectat/var/folders/12/mydd4m455zn9t__48syck4940000gn/T/failsafe-docs-home/project:[bundled]bundled/aws.rego2blockrule(s)bundled/failsafe.rego2blockrule(s)bundled/git.rego0blockrule(s)bundled/helm.rego2blockrule(s)bundled/kubectl.rego1blockrule(s)bundled/terraform.rego2blockrule(s)9blockrulestotal,0allow_override.bash-3.2$bash-3.2$ebash-3.2$exportPS1='$';bash-3.2$exportPS1='$';cdbash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9tbash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//facs-cs-home/project";clear$f$ex$exitexit \ No newline at end of file diff --git a/docs/assets/casts/explain-block.cast b/docs/assets/casts/explain-block.cast index 3811329..04aa745 100644 --- a/docs/assets/casts/explain-block.cast +++ b/docs/assets/casts/explain-block.cast @@ -1,14 +1,33 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354881} -[0.010649, "o", "\u001b[?2004h"] -[0.010887, "o", "root@vm:/home/user/failsafe# "] -[0.011159, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.012521, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] -[0.014478, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.014559, "o", "\u001b[?2004h$ "] -[0.524948, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] -[0.525498, "o", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete \r ns payments\"\r\n\u001b[?2004l\r"] -[0.546886, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /tmp/failsafe-docs-home/project\r\nMode: read\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked in read mode\r\n"] -[0.548369, "o", "\u001b[?2004h$ "] -[2.559258, "i", "exit\r"] -[2.559434, "o", "exit\r\n\u001b[?2004l\r"] -[2.559469, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1784331884} +[0.007669, "o", "\r\nThe default interactive shell is now zsh.\r\nTo update your account to use zsh, please run `chsh -s /bin/zsh`.\r\nFor more details, please visit https://support.apple.com/kb/HT208050.\r\n"] +[0.010914, "o", "\u001b[?1034h"] +[0.011090, "o", "bash-3.2$ "] +[0.016704, "i", "export PS1='$ '; cd \"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/project\"; clear\r"] +[0.016757, "o", "e"] +[0.016839, "o", "xport PS1='$ '; cd \"/var/fol"] +[0.016850, "o", "de"] +[0.016962, "o", "rs/12/mydd4m455zn9t__48sy"] +[0.017024, "o", "ck4940000gn/"] +[0.017032, "o", "T"] +[0.017127, "o", "//failsafe-do \rcs-hom"] +[0.017198, "o", "e/project\"; clear\r\n"] +[0.021285, "o", "\u001b[3J\u001b[H\u001b[2J"] +[0.021574, "o", "$ "] +[0.544513, "i", "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"\r"] +[0.544583, "o", "f"] +[0.544639, "o", "ailsafe explain \"k"] +[0.544713, "o", "ubectl --context "] +[0.544726, "o", "ar"] +[0.544847, "o", "n:aws:eks:us-east-1:"] +[0.544859, "o", "12"] +[0.544984, "o", "3456789012:cluste"] +[0.545062, "o", "r/prod de"] +[0.545137, "o", "lete \r ns pa"] +[0.545211, "o", "yments\"\r\n"] +[0.848167, "o", "── call 1: kubectl ──\r\nVerb: delete\r\nFlags:\r\n"] +[0.848216, "o", " context = arn:aws:eks:us-east-1:123456789012:cluster/prod\r\nPositional: ns payments\r\nEffective cwd: /var/folders/12/mydd4m455zn9t__48syck4940000gn/T/failsafe-docs-home/project\r\nMode: enabled\r\nPolicy chain (6 modules at this cwd):\r\n [bundled] bundled/aws.rego\r\n [bundled] bundled/failsafe.rego\r\n [bundled] bundled/git.rego\r\n [bundled] bundled/helm.rego\r\n [bundled] bundled/kubectl.rego\r\n [bundled] bundled/terraform.rego\r\nDecision: BLOCK\r\nReason : kubectl delete blocked while failsafe is enabled\r\n"] +[0.849408, "o", "$ "] +[2.882566, "i", "exit\r"] +[2.882745, "o", "e"] +[2.882788, "o", "xit\r\n"] +[2.882877, "o", "exit\r\n"] diff --git a/docs/assets/casts/explain-block.svg b/docs/assets/casts/explain-block.svg index d87fa2b..5caab03 100644 --- a/docs/assets/casts/explain-block.svg +++ b/docs/assets/casts/explain-block.svg @@ -1 +1 @@ -$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/tmp/failsafe-docs-home/projectMode:readPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedinreadmode$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file +Thedefaultinteractiveshellisnowzsh.Toupdateyouraccounttousezsh,pleaserun`chsh-s/bin/zsh`.Formoredetails,pleasevisithttps://support.apple.com/kb/HT208050.bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-do$$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddeletenspayments"──call1:kubectl──Verb:deleteFlags:context=arn:aws:eks:us-east-1:123456789012:cluster/prodPositional:nspaymentsEffectivecwd:/var/folders/12/mydd4m455zn9t__48syck4940000gn/T/failsafe-docs-home/projectMode:enabledPolicychain(6modulesatthiscwd):[bundled]bundled/aws.rego[bundled]bundled/failsafe.rego[bundled]bundled/git.rego[bundled]bundled/helm.rego[bundled]bundled/kubectl.rego[bundled]bundled/terraform.regoDecision:BLOCKReason:kubectldeleteblockedwhilefailsafeisenabled$exitbash-3.2$bash-3.2$ebash-3.2$exportPS1='$';cd"/var/folbash-3.2$exportPS1='$';cd"/var/foldebash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48sybash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/Tcs-homcs-home/project";clear$f$failsafeexplain"k$failsafeexplain"kubectl--context$failsafeexplain"kubectl--contextar$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:12$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluste$failsafeexplain"kubectl--contextarn:aws:eks:us-east-1:123456789012:cluster/proddenspa$eexit \ No newline at end of file diff --git a/docs/assets/casts/mode-toggle.cast b/docs/assets/casts/mode-toggle.cast index 84a9839..76a3cb8 100644 --- a/docs/assets/casts/mode-toggle.cast +++ b/docs/assets/casts/mode-toggle.cast @@ -1,28 +1,43 @@ -{"version":2,"width":92,"height":22,"timestamp":1782354884} -[0.003421, "o", "\u001b[?2004hroot@vm:/home/user/failsafe# "] -[0.011223, "i", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r"] -[0.011447, "o", "export PS1='$ '; cd \"/tmp/failsafe-docs-home/project\"; clear\r\n\u001b[?2004l\r"] -[0.012870, "o", "\u001b[H\u001b[2J\u001b[3J"] -[0.012987, "o", "\u001b[?2004h$ "] -[0.524665, "i", "failsafe mode get\r"] -[0.524865, "o", "failsafe mode get\r\n"] -[0.524894, "o", "\u001b[?2004l\r"] -[0.529962, "o", "read\t(default; no source resolved)\r\n"] -[0.530698, "o", "\u001b[?2004h"] -[0.530812, "o", "$ "] -[2.547466, "i", "failsafe mode set rw\r"] -[2.547696, "o", "failsafe mode set rw\r\n\u001b[?2004l\r"] -[2.553079, "o", "read \u0026 write\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[2.553864, "o", "\u001b[?2004h$ "] -[4.563007, "i", "failsafe mode get\r"] -[4.563219, "o", "failsafe mode get\r\n\u001b[?2004l\r"] -[4.568191, "o", "read \u0026 write\t(env)\r\n"] -[4.569172, "o", "\u001b[?2004h$ "] -[6.576488, "i", "failsafe mode set ro\r"] -[6.576756, "o", "failsafe mode set ro"] -[6.576771, "o", "\r\n\u001b[?2004l\r"] -[6.582397, "o", "read\t(file: /tmp/failsafe-docs-home/.config/failsafe/tty-1280)\r\n"] -[6.583490, "o", "\u001b[?2004h$ "] -[8.586430, "i", "exit\r"] -[8.586669, "o", "exit\r\n\u001b[?2004l\r"] -[8.586724, "o", "exit\r\n"] +{"version":2,"width":92,"height":22,"timestamp":1784331888} +[0.005927, "o", "\r\nThe default interactive shell is now zsh.\r\nTo update your account to use zsh, please run `chsh -s /bin/zsh`.\r\nFor more details, please visit https://support.apple.com/kb/HT208050.\r\n"] +[0.006178, "o", "\u001b[?1034h"] +[0.006212, "o", "bash-3.2$ "] +[0.014081, "i", "export PS1='$ '; cd \"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/project\"; clear\r"] +[0.014288, "o", "e"] +[0.014310, "o", "xport PS1='$"] +[0.014358, "o", " '; cd \"/var/folde"] +[0.014367, "o", "rs/"] +[0.014445, "o", "12/mydd4m455zn9t__48sy"] +[0.014452, "o", "ck"] +[0.014535, "o", "4940000gn/T//failsa"] +[0.014578, "o", "fe-do \rcs-home/"] +[0.014585, "o", "pr"] +[0.014589, "o", "oj"] +[0.014664, "o", "ect\"; clear\r\n"] +[0.016854, "o", "\u001b[3J\u001b[H\u001b[2J"] +[0.017117, "o", "$ "] +[0.539295, "i", "failsafe mode get\r"] +[0.539375, "o", "f"] +[0.539437, "o", "ailsafe mode get\r\n"] +[0.550818, "o", "enabled\t(default; no source resolved)\r\n"] +[0.551704, "o", "$ "] +[2.583063, "i", "failsafe mode set disabled\r"] +[2.583205, "o", "f"] +[2.583281, "o", "ailsafe mode set disabled\r\n"] +[2.598676, "o", "disabled\t(file: /var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/.claude/pane-mode/w0t1p0:E03E0B4D-6630-49D4-96E2-ADCE77DB2402)\r\n"] +[2.599698, "o", "$ "] +[4.614719, "i", "failsafe mode get\r"] +[4.614865, "o", "f"] +[4.614953, "o", "ailsafe mode get\r\n"] +[4.629334, "o", "disabled\t(file: /var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/.claude/pane-mode/w0t1p0:E03E0B4D-6630-49D4-96E2-ADCE77DB2402)\r\n"] +[4.630511, "o", "$ "] +[7.160456, "i", "failsafe mode set enabled\r"] +[7.160543, "o", "f"] +[7.160604, "o", "ailsafe mode set e"] +[7.160689, "o", "nabled\r\n"] +[7.171934, "o", "enabled\t(file: /var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/.claude/pane-mode/w0t1p0:E03E0B4D-6630-49D4-96E2-ADCE77DB2402)\r\n"] +[7.172671, "o", "$ "] +[9.711063, "i", "exit\r"] +[9.711225, "o", "e"] +[9.711286, "o", "xit\r\n"] +[9.711402, "o", "exit\r\n"] diff --git a/docs/assets/casts/mode-toggle.svg b/docs/assets/casts/mode-toggle.svg index ea0338c..22dbb04 100644 --- a/docs/assets/casts/mode-toggle.svg +++ b/docs/assets/casts/mode-toggle.svg @@ -1 +1 @@ -$$failsafemodegetread(default;nosourceresolved)$failsafemodesetrwread&write(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)read&write(env)$failsafemodesetroread(file:/tmp/failsafe-docs-home/.config/failsafe/tty-1280)$exitroot@vm:/home/user/failsafe#root@vm:/home/user/failsafe#exportPS1='$';cd"/tmp/failsafe-docs-home/project";clearexit \ No newline at end of file +Thedefaultinteractiveshellisnowzsh.Toupdateyouraccounttousezsh,pleaserun`chsh-s/bin/zsh`.Formoredetails,pleasevisithttps://support.apple.com/kb/HT208050.bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-do$$f$failsafemodegetenabled(default;nosourceresolved)$failsafemodesetdisableddisabled(file:/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/.claude/pane-mode/w0t1p0:E03E0B4D-6630-49D4-96E2-ADCE77DB2402)$failsafemodesetenabledenabled(file:/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsafe-docs-home/.claude/pane-mode/w0t1p0:E03E0B4D-6630-49D4-96E2-ADCE77DB2402)$exitbash-3.2$bash-3.2$ebash-3.2$exportPS1='$bash-3.2$exportPS1='$';cd"/var/foldebash-3.2$exportPS1='$';cd"/var/folders/bash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48sybash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syckbash-3.2$exportPS1='$';cd"/var/folders/12/mydd4m455zn9t__48syck4940000gn/T//failsacs-home/cs-home/prcs-home/projcs-home/project";clear$failsafemodesete$eexit \ No newline at end of file diff --git a/docs/demos/README.md b/docs/demos/README.md index 88b352d..4e67b43 100644 --- a/docs/demos/README.md +++ b/docs/demos/README.md @@ -6,7 +6,7 @@ SPDX-License-Identifier: CC-BY-4.0 # Documentation demos (asciicast automation) This directory documents the **recording** half of the docs flow. It pairs with -the doc-validation harness (`test/docs/`, PR #2): +the doc-validation harness (`test/docs/`): | Concern | Owner | What it guarantees | |---------|-------|--------------------| @@ -46,7 +46,7 @@ same "missing tool ⇒ skip, never a vacuous pass" rule the harness uses. | Cast | Command | Documents | |------|---------|-----------| | `explain-block.cast` | `failsafe explain "kubectl … delete ns payments"` | tutorials/getting-started, README — the guard blocking a destructive command | -| `mode-toggle.cast` | `failsafe mode get / set rw / set ro` | reference/modes, getting-started step 4 | +| `mode-toggle.cast` | `failsafe mode get / set disabled / set enabled` | reference/modes, getting-started step 4 | | `audit.cast` | `failsafe audit` | how-to/repo-policy — the policy chain in force | Add a scenario by appending one `record_scenario` block in the script; keep the diff --git a/docs/reference/modes.md b/docs/reference/modes.md index de33ab1..3cfb776 100644 --- a/docs/reference/modes.md +++ b/docs/reference/modes.md @@ -20,7 +20,7 @@ The `allow_override` mechanism is separate from mode: a repo policy can override Flipping the current pane between the two values with `mode get` / `mode set`: -![failsafe mode get, set rw, set ro](../assets/casts/mode-toggle.svg) +![failsafe mode get, set disabled, set enabled](../assets/casts/mode-toggle.svg) *Recording regenerated by [`scripts/record-docs.sh`](https://github.com/UndermountainCC/failsafe/blob/main/scripts/record-docs.sh).* diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md index 633de8e..cc5b77e 100644 --- a/docs/tutorials/getting-started.md +++ b/docs/tutorials/getting-started.md @@ -121,9 +121,9 @@ failsafe mode get disabled ``` -Flipping to write and back, recorded: +Flipping to `disabled` and back, recorded: -![failsafe mode get, set rw, set ro](../assets/casts/mode-toggle.svg) +![failsafe mode get, set disabled, set enabled](../assets/casts/mode-toggle.svg) Now run a read-only `kubectl get` to confirm allowed commands still pass through: diff --git a/scripts/record-docs.sh b/scripts/record-docs.sh index 98c430b..a9af27c 100755 --- a/scripts/record-docs.sh +++ b/scripts/record-docs.sh @@ -6,7 +6,7 @@ # recordings for the documentation site. # # This is the *recording* half of the docs automation flow. It is a sibling to -# the doc-validation harness (test/docs/, PR #2): that harness proves the +# the doc-validation harness (test/docs/): that harness proves the # copy-paste snippets in docs/ actually run against the real binary; this script # produces the demo recordings that *show* them running, from the same commands, # so the two never drift. @@ -123,12 +123,14 @@ record_scenario() { record_scenario "explain-block" -- \ "failsafe explain \"kubectl --context arn:aws:eks:us-east-1:123456789012:cluster/prod delete ns payments\"|Decision: BLOCK" -# 2. The read/write mode switch (reference/modes, tutorials/getting-started step 4). +# 2. The enabled/disabled mode switch (reference/modes, tutorials/getting-started step 4). +# Later steps use --wait-stable (no |text): their words already sit on the +# screen from earlier steps, so a text wait would fire before the output lands. record_scenario "mode-toggle" -- \ - "failsafe mode get|read" \ - "failsafe mode set rw|read & write" \ - "failsafe mode get|read & write" \ - "failsafe mode set ro|read" + "failsafe mode get|enabled" \ + "failsafe mode set disabled|disabled" \ + "failsafe mode get" \ + "failsafe mode set enabled" # 3. The policy chain in force (how-to/repo-policy, reference). record_scenario "audit" -- \