Tailscale Access Grants

Decide who can observe, mutate, and open shells through catch.

Use Tailscale grants to decide who can connect to catch and what they can do after they reach it.

Reachability is not authorization. Your workstation must reach the catch Tailscale address, and the app grant must allow the requested yeet permission.

Start With One Admin Grant

For first setup, keep the policy simple. Let Tailscale admins reach catch on its RPC port and give those admins every yeet permission. Tailscale's built-in admin autogroup is autogroup:admin.

You can split the roles later. First get the host enrolled with one admin grant.

{
  "tagOwners": {
    "tag:yeet": ["autogroup:admin"],
    "tag:catch": ["tag:yeet"],
    "tag:app": ["tag:yeet"],
  },
  "grants": [
    {
      "src": ["autogroup:admin"],
      "dst": ["tag:catch"],
      "ip": ["tcp:41548"],
      "app": {
        "yeetrun.com/app/yeet": [{ "allow": ["read", "manage", "ssh"] }],
      },
    },
    {
      "src": ["tag:catch"],
      "dst": ["tag:catch"],
      "ip": ["tcp:41548"],
    },
  ],
}

Run yeet init after this policy is active. First setup requires read, manage, and ssh so one admin grant covers enrollment, validation, and shell checks.

Use autogroup:admin directly in the grant src. Do not create a group:yeet-admins entry that contains autogroup:admin; Tailscale policy groups are for actual user identities when you define them in the policy file.

The second grant lets catch nodes talk to other catch nodes on the same port. Keep it if you run more than one catch host.

Permission Levels

PermissionAllowsCommon examples
readObserving catch stateyeet status, yeet info, logs, events, ISO pool and endpoint state, VM defaults, artifact hashes
manageChanging services or host stateDeploys, updates, removals, ISO pool and workload changes, yeet rm --clean, config changes, snapshots, VM lifecycle commands, catch upgrades, Tailscale service changes, local image pushes
sshOpening catch-mediated shellsyeet ssh, yeet ssh <svc>, commands run inside a service namespace

manage does not imply read, and ssh does not imply either one. Include every permission a role needs.

yeet ssh <vm> is the exception to remember. Yeet needs read to find the VM connection details, then the VM guest uses normal SSH keys for login. Catch-host and service shells still require ssh.

The same rule applies to an ISO VM: read reveals its Catch-routed connection metadata, and the guest SSH key authorizes login. Creating, changing, removing, repairing, or configuring ISO state requires manage.

Workstation Access

Catch joins your tailnet, but the local yeet CLI does not run its own Tailscale client. Tailscale grants decide what a caller may do after it reaches catch, but your workstation must still be able to reach the catch Tailscale address. In the normal setup, install Tailscale on the workstation and connect it to the same tailnet before testing grants.

Split Access Later

After the first host is working, split access if you need tighter roles. Keep the same app capability key and change the src groups and allow lists. When you define policy-file groups, list the users in those groups directly. Give each group only the permissions it needs.

{
  "groups": {
    "group:yeet-readers": ["reader@example.com"],
    "group:yeet-deployers": ["deployer@example.com"],
    "group:yeet-shell-admins": ["shell-admin@example.com"],
  },
  "grants": [
    {
      "src": ["group:yeet-readers"],
      "dst": ["tag:catch"],
      "ip": ["tcp:41548"],
      "app": {
        "yeetrun.com/app/yeet": [{ "allow": ["read"] }],
      },
    },
    {
      "src": ["group:yeet-deployers"],
      "dst": ["tag:catch"],
      "ip": ["tcp:41548"],
      "app": {
        "yeetrun.com/app/yeet": [{ "allow": ["read", "manage"] }],
      },
    },
    {
      "src": ["group:yeet-shell-admins"],
      "dst": ["tag:catch"],
      "ip": ["tcp:41548"],
      "app": {
        "yeetrun.com/app/yeet": [{ "allow": ["read", "ssh"] }],
      },
    },
  ],
}

If a user matches multiple grants, catch treats the allowed yeet permissions as one combined set.

Denied Commands

When catch denies a command, yeet prints the missing permission and links back to this page:

missing yeet permission "manage"; update your Tailscale grant for yeetrun.com/app/yeet:
https://yeetrun.com/docs/security/tailscale-access-grants

Update the matching Tailscale grant, wait for the policy to apply, then rerun the command from a workstation that can reach the catch hostname. If the error says manage, adding SSH reachability will not help; update the app permission instead.