🛞 steerd
Approve, reject — or correct — your AI coding agent's actions from your phone.
Claude Code keeps asking for permission, so you either babysit the terminal or approve everything blindly. steerd lets you hold the reins from your phone: when the agent wants to run a tool, you get a push notification, tap a link, and Approve, Reject with a correction, or even edit the command — then it keeps going.
No native app. No forking Claude Code. It plugs into Claude Code's built-in
PreToolUse hook and relays the decision through a tiny self-hosted server.
⚠️ Alpha / work in progress. Core scaffold is in place; see HANDOFF.md for status and the build plan.
How it works
Claude Code (laptop)
└─ PreToolUse hook → steerd relay → push to your phone
└─ you tap: ✅ Approve · ❌ Reject (+ correction) · ✏️ Edit command
└─ decision returns to Claude → it proceeds, stops, or adjusts
- Approve → the tool runs.
- Reject + correction → blocked, and your note is fed back to Claude as guidance.
- Edit input → rewrites the command before it runs (
updatedInput). - Auto-allow read-only tools (Read/Glob/Grep) so you're only pinged for the risky stuff.
Quickstart (local loop)
# Recommended: install globally with pipx (isolated deps, command on PATH)
pipx install steerd # or: pip install --user steerd
# 1. run the relay
steerd-relay
# 2. register the hook in ~/.claude/settings.json (see examples/)
# ⚠️ use the ABSOLUTE path to the hook — see note below
# 3. get phone pushes via ntfy (free, no account):
export STEERD_NTFY_TOPIC="your-unique-topic-name"
# install the "ntfy" app and subscribe to that topic
Use the absolute path for the hook command in
settings.json. The Claude Desktop app spawns hooks with a limitedPATH, so a baresteerd-hookcan fail to resolve there. Find the real path withwhich steerd-hook(e.g.~/.local/bin/steerd-hookafter a pipx install) and use that exact path in the hook'scommand.
Now run Claude Code — when it wants to run a tool, your phone buzzes; tap the link and decide.
📱 Using a real phone (not localhost)? See DEPLOY.md for exposing the relay via Tailscale / Cloudflare Tunnel / ngrok — and always set
STEERD_TOKENwhen you do.
Config (env vars)
| Var | Default | Purpose |
|---|---|---|
STEERD_TOKEN |
— | shared secret protecting the relay — set this before exposing the relay beyond localhost |
STEERD_RELAY_URL |
http://127.0.0.1:8787 |
where the hook reaches the relay |
STEERD_PUBLIC_URL |
= relay url | public link used in the push (use a tunnel/VPS for a remote phone) |
STEERD_TIMEOUT |
300 |
seconds to wait for a decision (keep < hook's 600s) |
STEERD_ON_TIMEOUT |
deny |
fallback if no answer: deny or allow |
STEERD_NTFY_TOPIC |
— | ntfy topic for push |
STEERD_TELEGRAM_BOT_TOKEN / _CHAT_ID |
— | Telegram backend (alt to ntfy) |
STEERD_AUTO_ALLOW |
Read,Glob,Grep,LS |
tools approved without pinging the phone |
Where to enable it (scope)
Install once, run one relay — then where you register the hook decides what it guards. It plugs into Claude Code's PreToolUse hook and works with both the Claude Desktop app and the CLI (both read these settings).
| Put the hook in… | Guards | Use for |
|---|---|---|
~/.claude/settings.json (global) |
every session, every project | supervise all agent shell commands |
<project>/.claude/settings.json (project) |
only sessions opened in that project | guard one repo (prod / infra / trading) |
<project>/.claude/settings.local.json (local) |
that project, just you (uncommitted) | personal, per-repo |
- A session loads hooks at startup, and only from its project root — not subfolders. Open the session in the folder whose settings has the hook, and restart after editing settings.
- Only Bash is gated by the example matcher; read-only tools are auto-approved — so you're pinged for shell commands, not every file read.
- Recommended: per-project on sensitive repos for day-to-day work; global when you're letting an agent run on its own.
Gotchas
- Long-running servers block. Approving
npm run devmakes Claude's terminal wait on a process that never exits — that's normal, not steerd. Ask Claude to start servers in the background. - Keep the relay up. If it's down, gated commands fall back to Claude's normal in-app prompt (graceful, not broken). For always-on, run the relay as a service — see DEPLOY.md.
Why not just…
--dangerously-skip-permissions? That approves everything, blind. This keeps you in control of the risky calls only.- Allowlists in settings.json? Great for known-safe commands — use both. steerd covers the unpredictable calls you can't pre-list, while away from the keyboard.
- A mobile app? Unnecessary — a push + web page does the job and stays installable in seconds.
Status & roadmap
Core relay + hook are scaffolded. Next: tests + CI, a remote-tunnel guide, an auth token on the relay, and a demo. See HANDOFF.md.
- End-to-end tested in Claude Code with phone approval
- Auth token on relay endpoints
- Tunnel/VPS guide for remote phones
- Demo GIF
- Support more agents (Cursor, Cline) via the same relay
License
MIT © Kannan Dharmalingam
Metadata
Release files for steerd 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| steerd-0.1.0.tar.gz | 16.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| steerd-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 27.9 kB
Release files / steerd-0.1.0.tar.gz
| Download URL | steerd-0.1.0.tar.gz |
|---|---|
| Size | 16.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
12edf489ab6733ea8f7df3b5a57ae1d1f74cad071610b4bab68a5d57905fb29c
|
|
BLAKE2b-256 checksum How to use checksums |
bcad363cb8b4f79f549e7f19c738d82d0fc116a75085e67c91d640beea593cec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / steerd-0.1.0-py3-none-any.whl
| Download URL | steerd-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
42c171463d90144aeba89a3ad61614b0476d2fa5923b265e804ad48c445b69dc
|
|
BLAKE2b-256 checksum How to use checksums |
c7f5488facb377af3e31deb11fe81fa68a5ea216d2725b98157edeb20ad2670a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Security for steerd 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.