Skip to content
DavidSling
All work

Valet

Lets an AI agent run commands on someone else's machine, while the machine's owner decides what it's allowed to do.

Role
Designed and built it
Status
Live on macOS, since Nov 2026
Stack
Rust, Tauri, TanStack Start
Runs on
macOS first
Every session starts with a handover. Drag the stub to tear it.

The problem

An agent is only as good as what it can see. The bugs that hurt most live on someone else's machine: an install that fails on one customer's laptop, a crash the reports don't explain.

The ways in weren't built for this. Remote desktop assumes a person watching a screen. SSH hands over an account's full rights at login. Agent tools like Claude Code ask the person who started them, on their own machine. None of them cover two parties: one who wants to look, and one who owns the machine.

Valet is a valet key. The agent gets to drive; it doesn't get to open the trunk.

How it works

  1. The agent starts a session. It reads a skill file, creates a session with curl, and hands its person a six-character code. No accounts on either side.
  2. The client reviews. They enter the code in the Valet app on their Mac, see who's asking and why, and choose what to allow up front: nothing, everything, or the sender's suggested set.
  3. Everything else asks. Anything outside those permissions lands on the client's screen with the exact command that will run. Medium risk takes a click; high risk takes Touch ID. Sessions end after an hour unless the client extends them.

Acme Support suggests these permissions

Anything you switch off asks you first instead.

Files

Programs

Processes

Starts with 5 permissions. Everything else asks.

Commands only: no screen sharing and no mouse or keyboard control, ever. Try a prompt below, and watch what the agent gets back.

Three things an agent might ask for

Network access always asks, even when the program is already allowed.

Acme Support's agent, with curl
POST /sessions/k7q2/actions
{"tool": "run_command",
 "args": {"command": ["/usr/bin/curl", "-s",
   "https://api.acme.com/v1/drivers/latest"]},
 "reason": "Checking the latest driver Acme recommends."}

← "pending"
// waiting for the person

Acme Support wants to run a command

curl -s https://api.acme.com/v1/drivers/latest

/usr/bin/curl in ~connect api.acme.com:443

Agent's reason

Checking the latest driver Acme recommends.

The decisions that mattered

Every call came back to one question: whose machine is it? Valet's answer is that the client decides, every time, on their own hardware.

  • The client's machine decides

    Grants and the allow-or-ask call live in the client app's Rust core. The relay in the middle carries actions back and forth and keeps a read-only copy of the grants. It never decides anything.

  • Enforce in the sandbox, not by reading commands

    Guessing what a shell command does from its text is a losing game. Every command runs under the macOS sandbox, with a profile built from the session's grants that starts from deny all. Credential folders like ~/.ssh and ~/.aws are denied again after every broad allow.

    What the command says

    /Applications/AcmeApp.app/Contents/MacOS/AcmeApp --diagnose

    A program the client allowed, with a harmless flag.

  • The screen can't approve anything

    The app's interface shows text an agent wrote, so it's treated as untrusted. It can only hand a choice back to Rust, which takes one answer per prompt, only after a 600 ms pause, and runs Touch ID itself.

  • Read before you write

    Every read returns a hash of the file, and every write must carry the hash it was based on. If the file changed in between, the write is turned back before anyone is asked.

    1. 1 read_file config.json → hash 7d1e…a40c
    2. 2 nothing touches the file
    3. 3 write_file config.json base_hash 7d1e…a40c

    - "gpu": "auto"

    + "gpu": "integrated"

  • Nothing outlives the session

    Tokens live in memory only, never on disk. Ending a session kills its processes and drops its grants, and everything on the relay expires on its own.

How I built it

Spec first, again. The product spec, the design system and the architecture were written before any code, and a clickable mockup of the whole session came before the real app. The permission engine was written in TypeScript for that mockup, then ported to Rust with shared test cases so the two can't drift apart.

The relay reuses Wave's plumbing: token handling, long-polling and rate limits, on the same Redis under its own prefix. Claude Code wrote the code, and every commit so far carries its co-author line. When a fresh agent tried the skill file cold, the places it stumbled went straight back into the skill.

What's next

Valet is days old, and it's live. The Mac app ships signed and notarised, and updates itself from each new release. Here's what's still open.

  • A signed log of every session

    Each action, decision and result chained by hash and signed on the client, so both sides can check what happened. Designed, not built yet.

  • End-to-end encryption

    Today the relay sees commands and output in plain text while a session is alive. It says so plainly until that changes.

  • Network grants per host

    Network access is all or nothing for now, and every network action asks.

  • Life after sandbox-exec

    macOS still ships it, but it's deprecated. Endpoint Security or a VM-based runner are the fallbacks.

  • Windows, then Linux

    The Mac comes first. Windows Hello takes the place of Touch ID.

Where it's heading: at the end of a session, both halves hold the same signed record.