Source profileQuality 91/100

magnus919/agent-skills/tailscale/skills/tailnet-policy/SKILL.md

tailnet-policy

Author, test, and deploy Tailscale-compatible huJSON policy files for Headscale tailnets — ACLs, Grants, Tags, Auto Approvers, Tailscale SSH rules. Use when configuring access control, writing policy files, or troubleshooting connectivity issues caused by ACLs.

Source repository stars
61
Declared platforms
0
Static risk flags
0
Last source update
2026-08-26
Source checked
2026-08-28

Decision brief

What it does: where it fits

Author, test, and deploy Tailscale-compatible huJSON policy files for Headscale tailnets — ACLs, Grants, Tags, Auto Approvers, Tailscale SSH rules.

Best for

  • Use when configuring access control, writing policy files, or troubleshooting connectivity issues caused by ACLs.

Not for

  • Do not use this skill for deploying the Headscale server (load headscale-deploy instead) or for client connectivity issues unrelated to access control (load tailscale-client). It covers huJSON policy authoring and testi…

Compatibility matrix

Platform support, with evidence labels

PlatformStatusEvidenceWhat to check
CodexNot declaredNo explicit evidencePortability before use
Claude CodeNot declaredNo explicit evidencePortability before use
CursorNot declaredNo explicit evidencePortability before use
Gemini CLINot declaredNo explicit evidencePortability before use
Open the compatibility checker

Installation

Inspect first. Install second.

The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

Source-detected install commandSource
npx skills add https://github.com/magnus919/agent-skills --skill "tailscale/skills/tailnet-policy"
Safe inspection promptEditorial

Inspect the Agent Skill "tailnet-policy" from https://github.com/magnus919/agent-skills/blob/531ff6753784823c878c92b988c6e55266ce09a9/tailscale/skills/tailnet-policy/SKILL.md at commit 531ff6753784823c878c92b988c6e55266ce09a9. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

Workflow

What the source asks the agent to do

  1. 01

    ACLs vs Grants

    Use Grants wherever possible. The migrate-acls-to-grants.py script can convert legacy ACL files automatically.

    Use Grants wherever possible. The migrate-acls-to-grants.py script can convert legacy ACL files automatically.
  2. 02

    Policy Location

    The policy file path is configured in Headscale's config.yaml:

    The policy file path is configured in Headscale's config.yaml:After modifying the policy file, reload it on the Headscale server:
  3. 03

    Reload via SIGHUP

    kill -HUP $(pgrep headscale)

    kill -HUP $(pgrep headscale)
  4. 04

    Or use the convenience script

    ./skills/tailnet-policy/reload-headscale-policy.sh hujson { // Grants that allow all traffic "grants": [ { "src": ["autogroup:member"], "dst": ["autogroup:member"], "ip": [":"] } ], // Tag ownership "tagOwners": { "tag:dev": ["autogroup:admin"], "tag:prod": ["autogroup:admin"] }…

    Headscale does NOT support device posture — rules like devicePosture or device:managed are Tailscale-onlyHeadscale does NOT support IP sets — ipSets and ipprotocol are not supportedHeadscale does NOT support OIDC groups in ACLs — groups from OIDC claims cannot be used in policy; use autogroup:admin and autogroup:member instead
  5. 05

    Writing Policy

    Tags are node-level identifiers set via tailscale up --advertise-tags=tag:dev. They decouple policy from user identity.

    Tags are node-level identifiers set via tailscale up --advertise-tags=tag:dev. They decouple policy from user identity.

Permission review

Static risk signals and limitations

No configured static risk pattern was detected

This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

Evidence record

Why each signal appears

EvidenceSourceComputedTestedEditorial
SignalValueEvidence typeMeaning
Quality score91/100ComputedDocumentation, specificity, maintenance, and trust rules
Repository stars61SourceRepository attention, not individual Skill quality
Compatibility0 platformsSourceDeclared in the catalog source record
Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

Pinned source

Provenance and original SKILL.md

Repository
magnus919/agent-skills
Skill path
tailscale/skills/tailnet-policy/SKILL.md
Commit
531ff6753784823c878c92b988c6e55266ce09a9
License
MIT
Collected
2026-08-28
Default branch
main
View the original SKILL.md

tailnet-policy

Overview

Headscale uses Tailscale-compatible policy files written in huJSON (Human JSON — standard JSON with trailing commas and // comments). Policy files control:

  • ACLs (deprecated legacy syntax) — {action, users, ports} rules
  • Grants (modern syntax) — {src, dst, ip, proto, via} rules
  • Tags — Node identity tags (tag:dev, tag:prod)
  • TagOwners — Which users/groups can apply which tags
  • AutoApprovers — Auto-approval for subnet routers and exit nodes
  • Tailscale SSH — SSH access rules via ssh.users and ssh.action

ACLs vs Grants

FeatureACLs (legacy)Grants (modern)
Format{action: "accept", users: [...], ports: [...]}{src: [...], dst: [...], ip: [...], proto: "tcp"}
Port filteringEmbedded in ports: ["*:*"]Separate ip field for ports
Protocol filteringNot supportedproto field (tcp, udp, icmp)
Destination routingNot supportedvia field for relay/exit nodes
StatusDeprecated by TailscaleCurrent recommended syntax

Use Grants wherever possible. The migrate-acls-to-grants.py script can convert legacy ACL files automatically.

Policy Location

The policy file path is configured in Headscale's config.yaml:

policy:
  path: /etc/headscale/policy.hujson

After modifying the policy file, reload it on the Headscale server:

# Reload via SIGHUP
kill -HUP $(pgrep headscale)

# Or use the convenience script
./skills/tailnet-policy/reload-headscale-policy.sh

Writing Policy

Allow-All (default-open)

{
  // Grants that allow all traffic
  "grants": [
    {
      "src": ["autogroup:member"],
      "dst": ["autogroup:member"],
      "ip": ["*:*"]
    }
  ],
  // Tag ownership
  "tagOwners": {
    "tag:dev": ["autogroup:admin"],
    "tag:prod": ["autogroup:admin"]
  }
}

Deny-All (default-closed)

{
  "grants": [
    // Only allow ICMP (ping) between all members
    {
      "src": ["autogroup:member"],
      "dst": ["autogroup:member"],
      "ip": ["*"],
      "proto": "icmp"
    }
  ],
  // Specific grants added per-service
  "tagOwners": {
    "tag:monitor": ["autogroup:admin"]
  }
}

Segmented (environments)

{
  "grants": [
    // Dev can reach dev
    {
      "src": ["tag:dev"],
      "dst": ["tag:dev"],
      "ip": ["*:*"]
    },
    // Prod can reach prod
    {
      "src": ["tag:prod"],
      "dst": ["tag:prod"],
      "ip": ["*:*"]
    },
    // Admin access to all
    {
      "src": ["autogroup:admin"],
      "dst": ["tag:dev", "tag:prod"],
      "ip": ["*:*"]
    }
  ],
  "tagOwners": {
    "tag:dev": ["autogroup:admin"],
    "tag:prod": ["autogroup:admin"]
  }
}

Tag-Based Patterns

Tags are node-level identifiers set via tailscale up --advertise-tags=tag:dev. They decouple policy from user identity.

{
  "tagOwners": {
    "tag:ci-runner":  ["autogroup:admin"],
    "tag:database":   ["autogroup:admin"],
    "tag:webserver":  ["autogroup:admin"],
    "tag:monitoring": ["autogroup:admin"]
  },
  "grants": [
    {
      "src": ["tag:monitoring"],
      "dst": ["tag:webserver", "tag:database"],
      "ip": ["*:*"]
    },
    {
      "src": ["tag:webserver"],
      "dst": ["tag:database"],
      "ip": ["tcp:5432"]
    }
  ]
}

Grants Syntax

Grants are the modern policy primitive:

{
  "grants": [
    {
      "src": ["tag:source", "[email protected]"],
      "dst": ["tag:destination", "100.64.0.1"],
      "ip": ["*:*"],                    // proto:port — "*:*" means all
      "proto": "tcp",                   // optional protocol filter
      "via": ["tag:exit-node"]          // optional via/routing
    }
  ]
}

Fields:

  • src — Source entities (tags, users, autogroups, IPs)
  • dst — Destination entities
  • ip — Protocol and port filter (e.g. tcp:80, udp:53, *:*, *)
  • proto — Protocol constraint (tcp, udp, icmp)
  • via — Route through a specific exit node or relay

Auto Approvers

Auto-approvers let specific users approve subnet routes and exit nodes without manual intervention:

{
  "autoApprovers": {
    "routes": {
      "10.0.0.0/8": ["autogroup:admin"],
      "172.16.0.0/12": ["[email protected]"]
    },
    "exitNode": ["autogroup:admin"]
  }
}
  • routes: Maps CIDR ranges to lists of users who can auto-approve those routes
  • exitNode: Lists users who can advertise exit nodes

Autogroups

Autogroups are dynamic groups resolved by Headscale/Tailscale at runtime:

AutogroupDescription
autogroup:memberAll tailnet members
autogroup:adminTailnet admins
autogroup:taggedAll tagged nodes (any node with at least one tag)
autogroup:internetThe public internet (used for exit node routing)

Tailscale SSH Configuration

Tailscale SSH rules are configured via the ssh section:

{
  "ssh": [
    {
      "action": "accept",         // "accept" or "check"
      "src": ["autogroup:admin"],
      "dst": ["tag:webserver"],
      "users": ["root", "ubuntu"]
    },
    {
      "action": "check",          // "check" requires node-level SSH authorization
      "src": ["autogroup:member"],
      "dst": ["tag:dev"],
      "users": ["*"]
    }
  ]
}
  • action: "accept" (allow directly) or "check" (require node-level auth)
  • src: Source users/groups
  • dst: Destination tags/users
  • users: Which OS users can be SSH'd into

Testing Policies

Policy files include test definitions that are validated when loaded:

{
  "grants": [...],
  "tests": [
    {
      "src": "[email protected]",
      "dst": "tag:webserver",
      "ip": ["tcp:443"],
      "action": "accept"  // expected result
    },
    {
      "src": "[email protected]",
      "dst": "tag:database",
      "ip": ["tcp:22"],
      "action": "drop"    // expected result
    }
  ]
}

Validate tests with:

./skills/tailnet-policy/validate-policy.py --policy policy.hujson

Gotchas

  • Headscale does NOT support device posture — rules like devicePosture or device:managed are Tailscale-only
  • Headscale does NOT support IP setsipSets and ipprotocol are not supported
  • Headscale does NOT support OIDC groups in ACLs — groups from OIDC claims cannot be used in policy; use autogroup:admin and autogroup:member instead
  • Tag names must start with tag: and contain only lowercase letters, numbers, and hyphens
  • Policy reload via SIGHUP does not return errors on failure — always validate before reloading
  • ACL users field and Grant src field are NOT interchangeable — grants use src/dst, legacy ACLs use users/ports
  • Tests are not enforced at runtime — they only validate during parsing; a passing test does not guarantee runtime behavior

Trigger Conditions

This skill is automatically loaded when the user's message contains any of these keywords:

  • tailnet policy
  • headscale acl
  • hujson policy
  • tailscale grant
  • tagowners
  • autoapprovers
  • tailscale ssh policy
  • policy validation
  • migrate acls
  • /etc/headscale/policy
  • policy.hujson

When not to use

Do not use this skill for deploying the Headscale server (load headscale-deploy instead) or for client connectivity issues unrelated to access control (load tailscale-client). It covers huJSON policy authoring and testing only.

Frequently asked questions

What to verify before installation and use

What does the tailnet-policy source document cover?

Author, test, and deploy Tailscale-compatible huJSON policy files for Headscale tailnets — ACLs, Grants, Tags, Auto Approvers, Tailscale SSH rules.

How do I install tailnet-policy?

The source record exposes this install command: npx skills add https://github.com/magnus919/agent-skills --skill "tailscale/skills/tailnet-policy". Inspect the command and pinned source before running it.

Alternatives

Compare before choosing