Tailscale Grants Matrix
Turn a Tailscale policy's grants into a source-to-destination matrix, unioning the ip and app capabilities that several rules give the same pair.
This tool handles input and primary processing in your browser and is designed not to send the input to QuickKit’s server.
Avoid live production secrets; use dummy or masked data where possible. Data handling details
Access matrix
| Source | Destination | ip | app | Per rule (ip / app / srcPosture) |
|---|
Unresolved references
How it works
Reads the grants block of a Tailscale policy file and produces a matrix of source and destination pairs. Grants replaced acls as the way to express access and carry two different things at once: ip, the protocol and port capabilities, and app, named application capabilities whose values the receiving application interprets. Reading grants as a rule list hides the thing that matters, because the same pair may appear in several rules and what a peer may finally do is the union of all of them. This tool computes that union per pair, but the union alone is not a safe summary, so every contributing rule is also listed with its own capabilities beside its own posture: a pair reached by tcp:22 behind a device posture and tcp:443 without one is not a pair where both ports are open unconditionally. Grants have no deny form, so the matrix is additive by construction: nothing later in the file takes access away.
Group sources are expanded from the same file, and an ipset: reference is checked against the file's own ipsets block, so an undeclared one is reported as a typo rather than assumed valid; its addresses are not expanded. tag:, autogroup:, and svc: references are labelled but not enumerated, because what they contain lives in the coordination server. A svc: reference is a Tailscale Service, which is a destination-only selector: it is classified and reported as invalid when a grant uses it as a source, but whether the service exists, is advertised, or is approved is never checked.
srcPosture is carried through per contributing rule rather than dropped, because a grant gated by a device posture is conditional access for traffic from native nodes in the tailnet and must not read as open access. A grant's effective posture is its own srcPosture when the grant has the key, and the policy's top-level defaultSrcPosture when it omits it — so a grant naming no posture is not unconditional in a policy that sets a default. An explicit srcPosture: [] is different from omitting the key: Tailscale documents it as the way to opt one grant out of the default, so that grant is shown as having no posture. A non-empty srcPosture replaces the default entirely rather than adding to it. srcPosture: null is rejected, because Tailscale does not define whether it means omitted or empty.
Where several grants cover the same pair, each rule's own posture is shown next to that rule's capabilities, and a pair also covered by a grant with no effective posture is called out: grants are additive and have no deny form, so what that rule conveys is reachable without a posture — even where a gated rule conveys the same capability, such as tcp:22 in both or tcp:22 gated beside * ungated. A posture gates the grant that names it, not the access the pair ends up with: only what no ungated grant conveys still needs a posture, and only for traffic from native nodes. The per-rule lines show what each grant conveys; this tool does not compare capabilities one by one. The posture rules themselves are defined in the postures block and are not evaluated here.
Posture conditions apply only to traffic from native Tailscale nodes in the same tailnet: traffic from shared nodes and from devices behind a subnet router is not checked against posture and is allowed when it matches the IP-based conditions (src, dst, protocol and port). Whether a source arrives that way depends on the tailnet, not the file, so this tool does not determine it.
A grant destination is used whole and never split on its colon — unlike an ACL destination, its ports are in ip, so tag:prod is the destination, not a host named tag on port prod. App capability names are listed but their values are not: a capability's value is a free-form object that only the receiving application defines, and displaying it as if it meant something here would be misleading. Policy files are HuJSON, so comments and trailing commas are accepted.
Nothing is uploaded, stored, logged, or placed in the URL, and no tailnet or API is contacted, so the matrix reflects the file and not live device state.