The tailnet policy
hermetic keeps three entries in your tailnet policy file and nothing else: the tag:hermetic owner (only when nobody else owns the tag — normally you pasted it), the ssh rule, and the acls rule that opens 22, 443 and 7434 on tagged nodes. The 22 is there because Tailscale checks the network ACL before the ssh policy: without it, tailscale ssh to an agent is refused however the ssh rule reads. They live between // hermetic:managed markers; every other byte of the file — comments, key order, your own rules — is written back exactly as it came.
Inside the markers is hermetic’s. Edit a line in there by hand and hermetic policy calls the block drifted, shows you the diff, and the next hermetic apply writes hermetic’s version back over yours. To keep a change, make it outside the block.
hermetic policy # scope, per-block state, and the diff a write would makehermetic plan policy # the same as a plan, steps and diffhermetic plan policy --json > policy.json && hermetic apply policy.json --yesThe write is conditional. plan policy records the policy’s ETag; apply re-reads the file and refuses with CONFLICT if it moved in between, so an edit someone made in the admin console is never silently overwritten. A plan that carries no ETag — because the policy could not be read, or Tailscale answered without one — is refused outright rather than written unconditionally; run hermetic plan policy again. Before writing, the candidate goes to Tailscale’s own /acl/validate, which runs your policy’s embedded tests — a policy Tailscale would reject never reaches the tailnet. hermetic policy, hermetic plan policy and hermetic doctor call the same endpoint, so all three are also running your embedded ACL tests, on Tailscale’s side and storing nothing. Each write appends a policy.apply event to _fleet with the ETag before and after, never the policy text.
The diff you are shown is only hermetic’s own blocks — the marker lines and what is between them, one hunk per key, headed @@ acls @@. Nothing from the rest of your file appears in it, including the lines immediately around the block, because that output travels into plans, JSON and logs and your policy file can name people.
hermetic doctor reports the same state as one informational line. It is never a finding: on a fleet whose OAuth client lacks policy_file there is nothing hermetic can do about it, and a report that says PROBLEMS for ever is one nobody reads.
If you keep the policy in git, run hermetic init --create --skip-policy and paste from hermetic policy instead — the snippet and the managed blocks are generated from the same source, so they cannot say different things.