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.
How examples are labeled
Section titled “How examples are labeled”bashis a command the reader runs (the install command also uses POSIX syntax).consoleis input the reader types at a prompt.outputis 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.
Before you start
Section titled “Before you start”- 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 --versionprints a version).seal protectneeds it;seal demoandseal statusdo not.
Check the last one first:
$ claude --versionIt 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:
SEAL_VERSION=v0.4.0artifact_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 ~/.localSeal installed-tree pin role: published-asset
installed seal 0.4.0 linux-x64store: /home/you/.local/lib/seal/store/261324816077d3ab04cd55640ec79b605f2845dadb7b56d057892111601ffe32command: /home/you/.local/bin/sealtree: 261324816077d3ab04cd55640ec79b605f2845dadb7b56d057892111601ffe32The 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.
The path through this guide
Section titled “The path through this guide”Read these in order the first time; each one stands alone afterwards.
- Choosing what to protect — the judgement
call: which tool earns the gate, what
seal protectchanges, and what it deliberately leaves alone. - What is protected right now — asking
the machine instead of remembering:
seal statusandseal doctor, every state they report, and what each one means. - Knowing it worked — the approval prompt line by line, what a refusal means, and how to check a receipt afterwards.
- 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.