Skip to content

The Seal operating guide

Seal puts an approval gate in front of a named set of tools on each selected MCP server in a project. When a healthy gate can show a fresh approval request for that tool, you see exactly what would run, and the call waits for approval — once, and only once. In a healthy, non-drifted wrapper, other tools keep working as if Seal were not there; drift, missing state, or a server-start failure refuses the server instead.

This guide is for using that gate day to day. It assumes you have seen a .mcp.json before and can run commands in a terminal, and nothing more. The install command was updated to chain verification before execution. Other commands shown here were actually run. Literal output blocks reproduce that run; blocks marked with an ellipsis or explanatory text are excerpts. The outputs were captured in a scratch project on 2026-08-15, so paths in them will differ from yours.

  • bash is a command the reader runs (the install command also uses POSIX syntax).
  • console is input the reader types at a prompt.
  • output is text the product prints.

Each command, input, and product output has its own fence, and these three role labels are used consistently. Other fences retain their language because they show specifications or data rather than something to run, type, or read as product output.

  • Seal supports install, demo, receipt checking and Protect on Linux x86-64 and macOS x64/arm64. Windows, Linux ARM and other platforms are unsupported.
  • Node 20 or newer.
  • Claude Code installed (claude --version prints a version). seal protect needs it; seal demo and seal status do not.

Check the last one first:

Terminal window
$ claude --version

It should print the installed Claude Code version.

Download and independently verify the pinned Linux x86-64 release, then install. Fetch the release’s SHA256SUMS asset from the same release. Copy the whole POSIX command, including the backslashes and && operators. For other supported platforms, see the install guide:

Terminal window
SEAL_VERSION=v0.4.0
artifact_name="seal-v0.4.0-linux-x64" \
&& artifact_sha256="5b49ea26d29b608fcb4e3e370062b96e8c4a81d7fb5ce1fd30a2cbe737c69d3b" \
&& artifact_bytes=6301771 \
&& sums_name="SHA256SUMS" \
&& sums_sha256="0552373fc3cb7f7257b4cf491395425a1ce2f7126cc60142a961f53ff29026ce" \
&& curl -fsSLO "https://github.com/velvetmonkey/seal/releases/download/$SEAL_VERSION/$sums_name" \
&& curl -fsSLO "https://github.com/velvetmonkey/seal/releases/download/$SEAL_VERSION/$artifact_name" \
&& if command -v shasum >/dev/null 2>&1; then sums_actual="$(shasum -a 256 "$sums_name")"; else sums_actual="$(sha256sum "$sums_name")"; fi \
&& test "${sums_actual%% *}" = "$sums_sha256" \
&& expected_record="$(awk -v name="$artifact_name" '$3 == name { print $1, $2, $3 }' "$sums_name")" \
&& test "$expected_record" = "$artifact_sha256 $artifact_bytes $artifact_name" \
&& if command -v shasum >/dev/null 2>&1; then actual_digest="$(shasum -a 256 "$artifact_name")"; else actual_digest="$(sha256sum "$artifact_name")"; fi \
&& test "${actual_digest%% *}" = "$artifact_sha256" \
&& actual_bytes="$(wc -c < "$artifact_name")" \
&& test "$actual_bytes" -eq "$artifact_bytes" \
&& chmod +x "$artifact_name" \
&& ./"$artifact_name" --sha256 "$artifact_sha256" --bytes "$artifact_bytes" --prefix ~/.local

Seal installed-tree pin role: published-asset

installed seal 0.4.0 linux-x64
store: /home/you/.local/lib/seal/store/261324816077d3ab04cd55640ec79b605f2845dadb7b56d057892111601ffe32
command: /home/you/.local/bin/seal
tree: 261324816077d3ab04cd55640ec79b605f2845dadb7b56d057892111601ffe32

The installer refuses to run without the --sha256 pin, on purpose: you are telling it which bytes you meant to install. Make sure ~/.local/bin is on your PATH, then run seal demo once — it walks the whole approve-once, replay-blocked story in about a minute, against a harmless built-in server, and asks for nothing but a y/N.

One limit to know before you rely on receipt checking: seal demo generates a temporary signing key for its run, while the protected Claude Code path creates or reuses a machine-local signing key. A checker result is only as meaningful as the source of the public key you supply; a key taken from the same machine establishes self-consistency, not that the recorded decision happened.

For the CI demo receipt, GitHub Actions provenance lets a signed-in GitHub reader check the runner and workflow that produced a published evidence archive. It does not establish the receipt is true.

Read these in order the first time; each one stands alone afterwards.

  1. Choosing what to protect — the judgement call: which tool earns the gate, what seal protect changes, and what it deliberately leaves alone.
  2. What is protected right now — asking the machine instead of remembering: seal status and seal doctor, every state they report, and what each one means.
  3. Knowing it worked — the approval prompt line by line, what a refusal means, and how to check a receipt afterwards.
  4. When something looks wrong — every refusal token Seal can print, what caused it, and what to do next.

One honest sentence to carry into all four pages: Seal is a gate, not a sandbox. Each wrapper controls its selected calls on its named server. Bash, the network, subprocesses, and calls outside those selected routes remain outside its gate. In the recorded Claude Code 2.1.251 dialog, the client folds the message-body line that states this boundary; the fold does not bring those other paths inside Seal. The current renderer also puts that boundary in the schema description, a channel that recording paints; the new layout still needs a fresh client acceptance run.

Previous: Evaluator walk. Up: Documentation map. Next: Choosing what to protect.