Documentation menu
On this page

Try it in 5 minutes

Time: 5 minutes · Needs: Linux 5.19 or newer (arm64 or x86_64), ssh installed, no root · Result: a command waits in the kernel and runs only after a signed approval

The quickest honest look at the gate. There is no agent and no phone here: wardend supervises a small shell command, and a second terminal plays the phone with a test key. Nothing on this page protects a real agent; for that, use the hardened install.

Releases are not signed. The installer checks the downloaded archive against the release's checksums.txt, both fetched from GitHub over TLS, and nothing more: it trusts GitHub and the TLS path, like any curl | sh.

If you have the WardenClaw app on a phone, the whole trial is one command after the install of step 1:

wardend wrap -- sh -c 'ls -d /tmp; ssh -V'

The first run prints a pairing QR code, waits for the phone, shows the device’s name and key fingerprint and asks Approve this device? [y/N]; the command starts only after y. Every launch outside an approved subtree then waits for a signature from the phone (in the app’s observe mode it signs them all while you watch; in manual you decide), and at the exit wardend prints one line: commands seen, asked, allowed, denied, expired, and where the journal is. Put your agent in place of the shell command: wardend wrap -- claude "fix the failing test". Reference: wardend wrap.

Here wardend runs as the same user as the command. The command cannot get past the exec gate, but it can read wardend’s files and edit its config between runs. The system install (hardened install, curl … | sudo sh) is the one with real separation: wardend as a root service, the agent as its own user.

The steps below need no phone: a second terminal signs with a test key.

1. Install for your own user

curl -fsSL https://wardenclaw.dev/install.sh | sh -s -- --single-user

The script checks the archive against the release’s checksums, asks for a confirmation and puts wardend into ~/.local/bin, the reference files and uninstall.sh into ~/.local/share/wardend, and an example config into ~/.wardend/config.json. It warns when ~/.local/bin is not in your PATH.

Check: wardend --help prints the usage and exits with code 0.

2. Run a command under the gate

wardend never decides on its own: every launch outside an approved subtree is a card, and a card nobody signs expires into EPERM. So the first step is a test device that can sign. Create it, then run a command that trusts it:

umask 077
mkdir -p ~/.wardend
wardend keygen > ~/.wardend/demo-device.txt   # test device; not in this shell's working directory
DEV=$(sed -n 's/^deviceId=//p' ~/.wardend/demo-device.txt)
PUB=$(sed -n 's/^pubkey=//p' ~/.wardend/demo-device.txt)
cd /tmp   # the command below must not start in the directory that holds the seed
wardend run --trust "$DEV:$PUB" --ttl 30s -- sh -c 'ls -d /tmp; git --version'

The sh -c is the supervised command itself and starts at once. ls is the first launch outside an approved subtree, a root card: it waits in the kernel, for 30 seconds at most (--ttl). In a second terminal that is not inside that command, sign every card with the test key as it comes, the way the app does in its observe mode:

wardend approve --key-file ~/.wardend/demo-device.txt --count 0 --timeout 60s

The first terminal prints /tmp and the git version, and at the exit a summary line, wardend: execs=… denied=0 latency p50=… p95=… max=… exit=0 (the start line before it names the pid, the child, the socket and the journal). wardend approve is a “device in a terminal”: it reads each waiting request from the socket, recomputes its digest from the envelope as the app does and signs the decision; --count 0 keeps signing until --timeout.

3. Make a command wait for a signature

In the first terminal, run a command that trusts the test device, with a launch that leaves the gate:

wardend run --trust "$DEV:$PUB" --ttl 30s --quiet \
  -- sh -c 'ls -d /etc; ssh -V; echo "ssh rc=$?"'

ls is a root card again. ssh is a delegating spawn (execution leaves the gate), so it is a card of its own even after ls was approved, with the risk hint remote on it. Within the 30 seconds, in the second terminal, sign both:

$ wardend approve --key-file ~/.wardend/demo-device.txt --count 2 --timeout 30s
allow wd-… argv=[ls -d /etc] -> map[decision:allow id:wd-… ok:true]
allow wd-805763d1887fcfb8a26269e7d38f1db9 argv=[ssh -V] -> map[decision:allow id:wd-805763d1887fcfb8a26269e7d38f1db9 ok:true]

The first terminal prints /etc, the OpenSSH version and ssh rc=0.

4. Deny it

Run the same wardend run command again. In the second terminal approve the first card and sign a deny for the second:

wardend approve --key-file ~/.wardend/demo-device.txt --match '^ls ' --count 1 --timeout 10s
wardend approve --key-file ~/.wardend/demo-device.txt --match '^ssh ' --deny --count 1 --timeout 10s

The first terminal prints:

/etc
sh: 1: ssh: Operation not permitted
ssh rc=126

The same happens when no answer comes within the TTL (journal reason ttl expired): without a signature the command does not run. There is no mode in which wardend asks nobody.

5. Check the journal

Every exec and every decision lands in an append-only journal. Each record is hashed into a chain and signed by the supervisor’s key:

wardend verify-journal ~/.wardend/journal.jsonl

On a valid chain it exits with code 0 and warns that the key came from the journal itself. To check authorship too, pass the supervisor’s public key with --pubkey: wardend verify-journal.

6. Clean up

rm ~/.wardend/demo-device.txt                        # the test seed
~/.local/share/wardend/uninstall.sh --single-user

Details: Uninstall the trial install.

What you saw, and what you did not

Next

  1. Install wardend on the server for real.
  2. Get the app and connect the phone.