Navigate the docs

For AI agents: the documentation index is at /llms.txt, and every page here has a markdown version at its URL plus .md.

Approvals & policy: who decides what runs

This page explains SafeClaw's per-action approval levels, policy sources, and passkey decisions.

Every brokered request gets exactly one decision:

LevelBehavior
allowproceeds, no ceremony
askone passkey approval, then reused for that rule (or floor class) for its window
ask-alwaysa passkey approval every single time
denyblocked unconditionally

When multiple sources have an opinion, the strictest wins (deny > ask-always > ask > allow), the same fail-safe family as cloud IAM systems.

Out of the box the read and write floors are allow: a key you add is usable by your agent right away. A service's rules tighten specific actions (sending email, deleting a repository) to ask or ask-always. A connection you define yourself has no rules, so everything on it is allowed until you add some or set its floors.

Where decisions come from

  1. Service rules. Each catalog service ships human-labeled, per-action rules ("Read email content", "Delete a repository") matched nginx-style against the request. Written by the recipe, overridable by you.
  2. Your overrides. Per connection, you can re-level any rule or set read/write floors.
  3. Floors. Connection, category, and global read/write defaults, derived from an objective fact (the HTTP method), not a vibe. GET, HEAD and OPTIONS are reads; every other method is a write. They apply when no rule matches.

There are no "risk scores". SafeClaw doesn't grade actions medium-spicy on your behalf; rules state what an action is, and the level states your decision about it.

deny is never a factory default. SafeClaw is a gate, not a block: the point is to let agents work, with you at the choke point that matters.

What a tap actually approves

Approvals ride SUDP, a passkey-signed single-use grant protocol. The consent card names the specific action from a curated template; your signature covers that operation, scope-bound, and nothing else. There is no session-wide approval token to steal, and an approval can't be replayed into a different request.

What an approval lets through afterwards is stated on its card, from signed fields. It always covers only the requesting agent, on that connection and host, and only the unit that asked:

  • ask-always: this one request; the next identical one asks again.
  • ask on a request a service scopes by its values (a [requests] shape, e.g. an amount): this identical request, same values, for the window.
  • ask from a rule (e.g. "Send email"): later requests that the same rule decides, for the rule's window, reads or writes like the approved one. A read approved under a rule never lets a write through. Another rule still asks.
  • ask from a read or write floor you set to ask: later reads (or writes) on that connection that no rule matches, for the floor's window. A read approval never lets a write through.

A request your policy allows never needs an approval to be authorized; if its credential is not loaded yet, approving it loads the credential for allowed requests only and lets no ask request through. Whether a credential is loaded never decides whether an ask request may run.

The daemon refuses an approval whose signed card differs from what it would grant, so the card is never a guess.

Boundaries refuse; they don't prompt

A request outside a connection's hosts, or against a deny rule, is refused loudly and immediately. So is a request on a value-scoped path covered by a connection rule you wrote that decides on something an approval there cannot pin: the body, or a field outside the path's [requests] scope. It does not become an approval popup. Prompts are reserved for actions you defined as sensitive; violations are not a negotiation. This is the anti-approval-fatigue stance: the fewer taps you see, the more each one means.

For agents

An approval-gated command fails with the approval link in its output. The agent surfaces the link, backgrounds sc op wait <op_id> (exit 0 means approved), and re-runs the same command. You tap; nobody types "done".