Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

42 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

plating banner

plating

Reproducible terminal demos and workflow diagrams for READMEs.

Build animated terminal demos from captured output or static workflow diagrams from JSON. Generated SVGs embed directly in GitHub with no browser runtime.

Install · Use it · Used by Brigade

PyPI version Python 3.10+ MIT license

Install

pipx install plating-cli
npm install -g svg-term-cli   # SVG renderer plating shells out to
plating render examples/plating-demo.json

What it does

Job What you get
Spec JSON steps + outputs Commands stay honest; animation is synthesized
Scan Refuse identity leaks Home paths, username, hostname, private IPs
Render Animated SVG embed GitHub README and sites as a plain img
Diagram Lay out a workflow from JSON Matching static SVGs across repositories
Verify Drift detection Re-run specs when CLI output changes

plating rendering a demo spec into an SVG

That recording was made by plating itself.

Use it

Write a spec, quickstart.json:

{
  "title": "quickstart",
  "width": 84,
  "steps": [
    { "command": "mytool --version", "output": "mytool 1.2.3\n" },
    { "command": "mytool build", "output_file": "build-output.txt" }
  ]
}

Render it:

plating render quickstart.json
# wrote quickstart.svg   (and quickstart.cast, the reproducible source)

Then embed quickstart.svg in your README or drop it into a site.

Workflow diagrams

plating workflow renders a constrained JSON document into the shared Escoffier Labs workflow style. Authors name columns, nodes, and connections; connections are forward-only, running from an earlier column to a later column. Plating owns the canvas, spacing, typography, colors, arrows, and accessible SVG metadata.

{
  "title": "Source to release",
  "eyebrow": "BUILD WORKFLOW",
  "description": "Tracked source passes through verification before release.",
  "columns": [
    {"title": "INPUT", "nodes": [{"id": "source", "label": "source"}]},
    {"title": "CHECK", "nodes": [{"id": "test", "label": "tests", "kind": "focus"}]},
    {"title": "OUTPUT", "nodes": [{"id": "release", "label": "release", "kind": "success"}]}
  ],
  "edges": [
    {"from": "source", "to": "test"},
    {"from": "test", "to": "release", "label": "pass"}
  ]
}
plating workflow examples/workflow.json
# plating: wrote examples/workflow.svg

Commit the JSON source and generated SVG together. Re-running the command with the same source produces the same bytes.

plating workflow runs a leak scan before writing. It scans the parsed input strings and the rendered SVG, prints each finding once, and exits 2 without writing the SVG or creating its parent directory. The scan_patterns key only adds rules to the default set.

Plating workflow: captured output and JSON pass through an identity scan and SVG renderer to produce a checked-in README image

Where each step's output comes from

In priority order:

In the step Output is
"output": "..." the literal string
"output_file": "path" a captured-output file (relative to the spec, confined under the spec directory)
"run": true (or plating render --run) the live result of running command

Live capture (--run) is convenient; committing captured output is what makes it reproducible in CI. Use normalize to rewrite a throwaway path into something clean:

{ "normalize": [["/tmp/tmp.AbC123/demo", "~/my-repo"]] }

normalize rules are applied to the command and output shown in the recording. Live execution always runs the original, unnormalized command string or argv array, so a normalized display path never breaks the actual run.

output_file confinement

output_file is resolved under the spec's directory. Absolute paths, .. traversal, and symlink escapes are rejected with a plating: error before any artifact is written. A normal nested path inside the spec directory (e.g. "captures/out.txt") works as expected.

On POSIX systems with O_DIRECTORY and O_NOFOLLOW, paths are opened component-by-component through directory descriptors so symlink swaps after validation are rejected. Windows and other platforms without that capability cannot use output_file. Use a literal "output" value instead. Literal backslashes in POSIX filenames are preserved and are not treated as path separators.

Sanitization

Before rendering, plating scans the recording for /home/... and /Users/... paths, the machine's current username and hostname, private IPs, and a few narrow secret shapes (SOMETHING_TOKEN=... / SOMETHING_API_KEY=... / SOMETHING_SECRET=... / SOMETHING_PASSWORD=... assignments and PEM -----BEGIN ... PRIVATE KEY----- headers). Secret findings are redacted so the value is never echoed back. If the scan finds one it refuses to render and tells you how to fix it with a normalize rule or an explicit --allow-leaks override.

This scan is best-effort and dependency-free. It catches common shapes that leak into a recording. It is not a secrets scanner, so use a dedicated scanner for sensitive material.

plating scan some-recording.cast

Live runs (--run)

When a step runs live, plating parses the command into an argv list and runs it with shell=False. Consequences:

  • Use argv arrays for non-trivial commands. A command string is split with shlex (POSIX rules). On Windows, strings containing backslashes are rejected. POSIX also rejects ambiguous drive and UNC forms such as C:\... and \\.... Use an explicit JSON argv array for quoted arguments, empty strings, and backslash paths.
  • No shell operators. Pipes (|), redirects (>), &&, ;, backticks, and $() substitutions are not supported in live capture. Use a real script for anything that needs a shell.
  • Empty or malformed commands are rejected (e.g. an unterminated quote).
  • cwd is confined on POSIX. A spec-declared cwd is resolved relative to and confined within the spec directory using stable directory descriptors. Windows and other platforms without O_DIRECTORY + O_NOFOLLOW reject spec-declared cwd before subprocess start. The CLI --cwd argument may point anywhere on disk (not confined to the spec directory), but must exist and be a directory.
  • Environment is an explicit allowlist. Live runs inherit only PATH, LANG, LC_ALL, LC_CTYPE, TERM, COLORTERM, NO_COLOR, and (when present) the Windows launch variables SYSTEMROOT, WINDIR, COMSPEC, PATHEXT. HOME, cloud-provider, CI, SSH, and token variables are not inherited.
  • Timeout is enforced. Each live step gets a positive finite timeout, default 30 seconds. Set it with the spec's run_timeout key or the CLI --timeout flag (CLI wins). A timeout is reported as a plating: error, not a traceback.

Options

Spec keys: title, width, height, padding, window (macOS chrome, on by default), prompt, prompt_color, the timing knobs (type_speed, line_delay, command_pause, ... see src/plating/cast.py), normalize, scan_patterns, cwd, run_timeout.

CLI:

plating render <spec> [--run] [--cwd DIR] [--timeout SECONDS] [--out-dir DIR] [--png MS] [--allow-leaks]
plating scan <file>
plating workflow <spec> [--out FILE]

--png MS writes a static PNG of the frame at MS milliseconds (via headless Chrome), handy for a quick eyeball before you commit the SVG. --timeout SECONDS overrides the spec's run_timeout for live runs.

A real example

examples/brigade-quickstart.json rebuilds the quickstart recording used in the Brigade README from its real, captured output:

plating render examples/brigade-quickstart.json

License

MIT. See LICENSE.

About

README terminal demos without recording leaks. Reproducible, sanitized animated SVGs from JSON specs and real command output.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages