You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(tailscale): align tool coverage and outputs with the Tailscale API (#5366)
* fix(tailscale): align tool coverage and outputs with the Tailscale API
- fix list_users profilePicUrl field name (API returns lowercase, was always null)
- add nodeId, keyExpiryDisabled, expires to device outputs
- quote the If-Match header value on ACL updates per API spec
- add set_acl, expire_device_key, suspend_user, delete_user tools
- add wandConfig to dnsServers/searchPaths block fields
- expand BlockMeta skills/templates for ACL and key-expiry workflows
* fix(tailscale): remove phantom magicDNS field from list_dns_nameservers
The GET /tailnet/{tailnet}/dns/nameservers response only returns
{dns: string[]} per the API spec — magicDNS is not part of this
endpoint's response and was always silently false.
* fix(tailscale): extend ToolResponse in expire_device_key response type
Matches the pattern used by the other new tools in this PR
(delete_user, suspend_user).
* fix(tailscale): list_auth_keys now returns all tailnet keys
GET /tailnet/{tailnet}/keys silently scopes to the caller's own
keys unless all=true is passed, contradicting the tool's stated
purpose of listing all auth keys in the tailnet.
'Generate a comma-separated list of DNS nameserver IP addresses (e.g., 8.8.8.8,8.8.4.4). Return ONLY the comma-separated IP addresses - no explanations, no extra text.',
'Generate a comma-separated list of DNS search path domains (e.g., corp.example.com,internal.example.com). Return ONLY the comma-separated domains - no explanations, no extra text.',
'Create a scheduled workflow that diffs Tailscale ACLs against the source of truth, alerts on drift, and writes the drift report to Slack.',
411
+
'Create a scheduled workflow that diffs Tailscale ACLs against the source of truth, alerts on drift to Slack, and, on approval, pushes the corrected policy back with Set ACL.',
"Create a workflow that on a Workday termination deletes the departing engineer's Tailscale devices, revokes their auth keys, and writes the security audit log.",
431
+
"Create a workflow that on a Workday termination deletes the departing engineer's Tailscale devices, revokes their auth keys, suspends their tailnet user account, and writes the security audit log.",
description: 'Deauthorize or remove a departing user device and revoke its auth keys.',
484
+
description:
485
+
"Deauthorize or remove a departing user's devices and auth keys, then suspend or delete their tailnet account.",
486
+
content:
487
+
'# Offboard a Tailscale Device\n\nRemove a device from the tailnet during offboarding so access is cut cleanly.\n\n## Steps\n1. Use List Devices to find the deviceId tied to the departing user.\n2. To immediately cut access use Authorize Device set to Deauthorize, or Delete Device to remove it entirely.\n3. Use List Auth Keys to find any keys the user created, then Delete Auth Key for each.\n4. Use List Users to find the userId, then Suspend User to freeze access reversibly, or Delete User to remove the account entirely.\n5. Capture the device detail with Get Device before deletion if you need an audit record.\n\n## Output\nConfirm the device was deauthorized or deleted, the auth keys were revoked, and the user account was suspended or deleted for the offboarding audit log.',
488
+
},
489
+
{
490
+
name: 'update-tailnet-acl',
491
+
description:
492
+
'Push an updated ACL policy file to the tailnet using ETag-guarded writes to avoid clobbering concurrent edits.',
493
+
content:
494
+
'# Update the Tailnet ACL as Policy-as-Code\n\nApply a reviewed ACL change programmatically instead of editing it by hand in the admin console.\n\n## Steps\n1. Use Get ACL to fetch the current policy file and its etag.\n2. Compute or generate the new policy JSON from your source of truth (git, agent-authored rules, etc.).\n3. Use Set ACL with the new ACL Policy JSON, passing the etag from step 1 in If-Match to guard against concurrent updates (use "ts-default" instead if you only want to replace an untouched default policy).\n4. If the write fails with a precondition error, re-fetch the ACL and retry.\n\n## Output\nReturn the updated ACL JSON and its new etag, plus a summary of what changed for the change-management record.',
495
+
},
496
+
{
497
+
name: 'lock-down-compromised-device',
498
+
description:
499
+
"Immediately expire a suspected-compromised device's node key so it must re-authenticate before rejoining the tailnet.",
433
500
content:
434
-
'# Offboard a Tailscale Device\n\nRemove a device from the tailnet during offboarding so access is cut cleanly.\n\n## Steps\n1. Use List Devices to find the deviceId tied to the departing user.\n2. To immediately cut access use Authorize Device set to Deauthorize, or Delete Device to remove it entirely.\n3. Use List Auth Keys to find any keys the user created, then Delete Auth Key for each.\n4. Capture the device detail with Get Device before deletion if you need an audit record.\n\n## Output\nConfirm the device was deauthorized or deleted and list the revoked auth keys for the offboarding audit log.',
501
+
"# Lock Down a Compromised Device\n\nCut off a device the moment it looks compromised, without waiting for its key to expire naturally.\n\n## Steps\n1. Use Get Device or List Devices to confirm the deviceId and review its tags, addresses, and lastSeen.\n2. Use Expire Device Key to immediately invalidate the device's node key so it can no longer connect until it re-authenticates.\n3. For a harder block, follow up with Authorize Device set to Deauthorize, or Delete Device to remove it outright.\n4. Log the deviceId, hostname, and user in the incident record.\n\n## Output\nConfirm the key was expired (and the device deauthorized/deleted if applicable) for the security incident log.",
0 commit comments