{"id":1188,"date":"2022-09-27T10:45:48","date_gmt":"2022-09-27T15:45:48","guid":{"rendered":"https:\/\/www.brunerd.com\/blog\/?p=1188"},"modified":"2025-02-04T14:47:19","modified_gmt":"2025-02-04T19:47:19","slug":"determining-icloud-private-relay-and-limit-ip-tracking-status-in-macos","status":"publish","type":"post","link":"https:\/\/www.brunerd.com\/blog\/2022\/09\/27\/determining-icloud-private-relay-and-limit-ip-tracking-status-in-macos\/","title":{"rendered":"Determining &#8220;iCloud Private Relay&#8221; and &#8220;Limit IP tracking&#8221; status in macOS"},"content":{"rendered":"\n<p>If you are a Mac admin you might have noticed some Apple plists are more complex than others these days. As evidenced in my post <a href=\"https:\/\/www.brunerd.com\/blog\/2022\/03\/07\/respecting-focus-and-meeting-status-in-your-mac-scripts-aka-dont-be-a-jerk\/\" data-type=\"post\" data-id=\"1104\">Don&#8217;t Be a Jerk<\/a>, getting the status of Do Not Disturb in <strong>Big Sur<\/strong> was a multi-step exercise. Check out this modified code excerpt from <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/doNotDisturb.sh\" target=\"_blank\">doNotDisturb<\/a>&#8216;s <strong>Big Sur<\/strong> handling<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/sh\n\n#Big Sur DnD status, returns \"true\" or &#91;blank] (to be run as console user)\ndndStatus=\"$(\/usr\/libexec\/PlistBuddy -c \"print :userPref:enabled\" \/dev\/stdin 2&gt;\/dev\/null &lt;&lt;&lt; \"$(plutil -extract dnd_prefs xml1 -o - \/dev\/stdin &lt;&lt;&lt; \"$(defaults export com.apple.ncprefs.plist -)\" | xmllint --xpath \"string(\/\/data)\" - | base64 --decode | plutil -convert xml1 - -o -)\")\"\n\n#if we have ANYTHING it is ON (return 0) otherwise fail (return 1)\n&#91; -n \"${dndStatus}\" ] &amp;&amp; { echo ON; exit 0; } || { echo OFF; exit 1; }\n<\/code><\/pre>\n\n\n\n<p>Why is it so complex? Well because with the user preference domain of <code>com.apple.ncprefs<\/code> there is a base64 encoded plist embedded <em>in<\/em> the <code>dnd_prefs<\/code> key and requires some massaging to get it to a state where <code>PlistBuddy<\/code> can read and then extract the status from the <code>:userPref:enabled<\/code> key. See it&#8217;s just that easy! \ud83d\ude05<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"909\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction-1024x909.png\" alt=\"\" class=\"wp-image-1218\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction-1024x909.png 1024w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction-300x266.png 300w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction-768x682.png 768w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/Big-Sur-DND-status-extraction.png 1236w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>In macOS Monterey and higher Apple is now using JSON for Focus status which is <strong>great<\/strong> because it can can be parsed <em>much<\/em> easier (perhaps using my <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/ljt\" target=\"_blank\">ljt<\/a> or <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/ljt\" target=\"_blank\">jpt<\/a> tools) but sometimes not even <em>that<\/em> is needed! Sometimes file presence or awking is sufficient to get a reliable result too. See the evolution of <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/doNotDisturb.sh\">doNotDisturb.sh<\/a> for macOS for the myriad techniques one can use.<\/p>\n\n\n\n<p>Now, here we are at Monterey on the verge of Ventura and not all areas of macOS have seen the JSON light and are still using nested base64-encoded Plist blobs to store the states of <em>new<\/em> features like <strong>iCloud Private Relay<\/strong> but luckily <em>not<\/em> the per interface setting of <strong>Limit IP Tracking<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">iCloud Private Relay Status<\/h2>\n\n\n\n<p>Now why would you want to know these statuses? Perhaps you have a VPN product that doesn&#8217;t work when Private Relay is turned on or if Limit IP Tracking is turned on for a particular interface and you need to alert the user. Additionally maybe there&#8217;s an issue if your VPN browser handshake fails when Safari is used, if so see: <a href=\"https:\/\/www.brunerd.com\/blog\/2022\/09\/21\/determining-url-scheme-handlers-in-macos\/\" data-type=\"post\" data-id=\"1195\">Determining URL scheme handlers in macOS<\/a>. Back to the task at hand though let&#8217;s turn some prefs inside out and look at <strong>iCloud Relay<\/strong> status with <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudPrivateRelayStatus.sh\">iCloudPrivateRelayStatus.sh<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n: &lt;&lt;-LICENSE_BLOCK\niCloud Private Relay Status Checker (20250204) - (https:\/\/github.com\/brunerd)\nCopyright (c) 2025 Joel Bruner (https:\/\/github.com\/brunerd)\nLicensed under the MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and\/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nLICENSE_BLOCK\n\n#############\n# FUNCTIONS #\n#############\n\n#include this self-contained function in your script\nfunction iCloudPrivateRelay()(\n\n\t#only for Moneterey and up, 11 and under need not apply\n\t&#91; \"$(sw_vers -productVersion | cut -d. -f1)\" -le 11 ] &amp;&amp; return 1\n\t\n\t#parent pref domain\n\tdomain=\"com.apple.networkserviceproxy\"\n\t#key that contains base64 encoded Plist within parent domain\n\tkey=\"NSPServiceStatusManagerInfo\"\n\t#path within base64 embedded plist, there are multiple PrivacyProxyServiceStatus entries but this one seems primary\n\ttargetPath=':$objects:1:PrivacyProxyServiceStatus'\n\n\t#get the top level data from the main domain\n\tparentData=$(launchctl asuser \"$(stat -f %u \/dev\/console)\" sudo -u \"$(stat -f %Su \/dev\/console)\" defaults export \"${domain}\" -)\n\n\t#if domain does not exist, fail\n\t&#91; -z \"${parentData}\" ] &amp;&amp; return 1\n\n\t#export and decode the base64 data as plist XML and look for \n\tkeyStatusCF=$(\/usr\/libexec\/PlistBuddy -c \"print '${targetPath}'\" \/dev\/stdin 2&gt;\/dev\/null &lt;&lt;&lt; \"$(plutil -extract \"${key}\" xml1 -o - \/dev\/stdin &lt;&lt;&lt; \"${parentData}\" | xmllint --xpath \"string(\/\/data)\" - | base64 --decode | plutil -convert xml1 - -o -)\")\n\t#if no value, fail\n\t&#91; -z \"${keyStatusCF}\" ] &amp;&amp; return 1\n\t\n\t#if true\/1 it is on, 0\/off (value is integer not boolean BTW)\n\t&#91; \"${keyStatusCF}\" = \"1\" ] &amp;&amp; return 0 || return 1\n)\n\n########\n# MAIN #\n########\n\n#example - multi-line if\/else calling\nif iCloudPrivateRelay; then\n\techo \"iCloud Private Relay is: ON\"\nelse\n\techo \"iCloud Private Relay is: OFF\"\nfi<\/code><\/pre>\n\n\n\n<p>Even though we work really hard to get the plist data extracted <s>we don&#8217;t need to walk the entire XML document (you will find PlistBuddy balks at exporting JSON for a number of reasons). Instead we look for the presence of the key name <code>PrivacyProxyServiceStatus<\/code> and that is sufficient to reliably detect the state.<\/s> UPDATE: This ambiguity has been resolved and it will now look at the path <code>:$objects:1:PrivacyProxyServiceStatus<\/code> for the status, which seems to be consistent (LMK otherwise). I have 2 versions in my GitHub <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudPrivateRelayStatus.sh\">iCloudPrivateRelayStatus.sh<\/a> and the minified one line version <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/iCloudPrivateRelayStatus.min.sh\">iCloudPrivateRelayStatus.min.sh<\/a><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/iCloudPrivateRelayStatus-On.png\"><img loading=\"lazy\" decoding=\"async\" width=\"454\" height=\"94\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/iCloudPrivateRelayStatus-On.png\" alt=\"\" class=\"wp-image-1225\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/iCloudPrivateRelayStatus-On.png 454w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/iCloudPrivateRelayStatus-On-300x62.png 300w\" sizes=\"auto, (max-width: 454px) 100vw, 454px\" \/><\/a><\/figure>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Limit IP Tracking Status<\/h2>\n\n\n\n<p>Limit IP Tracking, is a per-interface setting that is <strong>on<\/strong> by default, however has no effect unless Private Relay is enabled. For this we will use <a href=\"https:\/\/github.com\/brunerd\/ljt\" target=\"_blank\" rel=\"noreferrer noopener\">ljt<\/a> my Little JSON Tool to retrieve the value from the massaged JSON conversion of <code>com.apple.wifi.known-networks.plist<\/code> if on WiFi or if on Ethernet <code>...\/SystemConfiguration\/preferences.plist<\/code>. (Update: Now macOS Sequoia compatible after Apple broke the usual way of getting the SSID name for WiFi connections)<\/p>\n\n\n\n<pre class=\"wp-block-code language-bash\"><code>#!\/bin\/bash\n: &lt;&lt;-LICENSE_BLOCK\nLimit IP Tracking Status Checker (20250204) - (https:\/\/github.com\/brunerd)\nCopyright (c) 2025 Joel Bruner (https:\/\/github.com\/brunerd)\nLicensed under the MIT License\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and\/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\nLICENSE_BLOCK\n\n#############\n# FUNCTIONS #\n#############\n\n#include this self-contained function in your script\nfunction limitIPTracking()(\n\n\t#only for Moneterey and up, 11 and under need not apply\n\t&#91; \"$(sw_vers -productVersion | cut -d. -f1)\" -le 11 ] &amp;&amp; return 1\n\n\t#Little JSON Tool (ljt) - https:\/\/github.com\/brunerd\/ljt - MIT License\n\tfunction ljt () ( #v1.0.8 ljt &#91;query] &#91;file]\n\t{ set +x; } &amp;&gt; \/dev\/null; read -r -d '' JSCode &lt;&lt;-'EOT'\n\ttry{var query=decodeURIComponent(escape(arguments&#91;0]));var file=decodeURIComponent(escape(arguments&#91;1]));if(query===\".\")query=\"\";else if(query&#91;0]===\".\"&amp;&amp;query&#91;1]===\"&#91;\")query=\"$\"+query.slice(1);if(query&#91;0]===\"\/\"||query===\"\"){if(\/~&#91;^0-1]\/g.test(query+\" \"))throw new SyntaxError(\"JSON Pointer allows ~0 and ~1 only: \"+query);query=query.split(\"\/\").slice(1).map(function(f){return\"&#91;\"+JSON.stringify(f.replace(\/~1\/g,\"\/\").replace(\/~0\/g,\"~\"))+\"]\"}).join(\"\")}else if(query&#91;0]===\"$\"||query&#91;0]===\".\"&amp;&amp;query&#91;1]!==\".\"||query&#91;0]===\"&#91;\"){if(\/&#91;^A-Za-z_$\\d\\.\\&#91;\\]'\"]\/.test(query.split(\"\").reverse().join(\"\").replace(\/(&#91;\"'])(.*?)\\1(?!\\\\)\/g,\"\")))throw new Error(\"Invalid path: \"+query);}else query=query.replace(\"\\\\.\",\"\\udead\").split(\".\").map(function(f){return\"&#91;\"+JSON.stringify(f.replace(\"\\udead\",\".\"))+\"]\"}).join(\"\");if(query&#91;0]===\"$\")query=query.slice(1);var data=JSON.parse(readFile(file));try{var result=eval(\"(data)\"+query)}catch(e){}}catch(e){printErr(e);quit()}if(result!==undefined)result!==null&amp;&amp;result.constructor===String?print(result):print(JSON.stringify(result,null,2));else printErr(\"Path not found.\")\n\tEOT\n\tqueryArg=\"${1}\"; fileArg=\"${2}\";jsc=$(find \"\/System\/Library\/Frameworks\/JavaScriptCore.framework\/Versions\/Current\/\" -name 'jsc');&#91; -z \"${jsc}\" ] &amp;&amp; jsc=$(which jsc);&#91; -f \"${queryArg}\" -a -z \"${fileArg}\" ] &amp;&amp; fileArg=\"${queryArg}\" &amp;&amp; unset queryArg;if &#91; -f \"${fileArg:=\/dev\/stdin}\" ]; then { errOut=$( { { \"${jsc}\" -e \"${JSCode}\" -- \"${queryArg}\" \"${fileArg}\"; } 1&gt;&amp;3 ; } 2&gt;&amp;1); } 3&gt;&amp;1;else &#91; -t '0' ] &amp;&amp; echo -e \"ljt (v1.0.8) - Little JSON Tool (https:\/\/github.com\/brunerd\/ljt)\\nUsage: ljt &#91;query] &#91;filepath]\\n  &#91;query] is optional and can be JSON Pointer, canonical JSONPath (with or without leading $), or plutil-style keypath\\n  &#91;filepath] is optional, input can also be via file redirection, piped input, here doc, or here strings\" &gt;\/dev\/stderr &amp;&amp; exit 0; { errOut=$( { { \"${jsc}\" -e \"${JSCode}\" -- \"${queryArg}\" \"\/dev\/stdin\" &lt;&lt;&lt; \"$(cat)\"; } 1&gt;&amp;3 ; } 2&gt;&amp;1); } 3&gt;&amp;1; fi;if &#91; -n \"${errOut}\" ]; then \/bin\/echo \"$errOut\" &gt;&amp;2; return 1; fi\n\t)\n\t\n\t#sequoia broke the old ways of getting SSID\n\tfunction getCurrentWiFiSSIDforInterface(){\n\t\t#no interface, error\n\t\t&#91; -z \"${1}\" ] &amp;&amp; return 1\n\n\t\t#get the SSID _quickly_ thanks MacAdmins @jby\n\t\tlocal currrent_SSID=$(ipconfig getsummary \"${1}\" | awk -F ' SSID : ' '\/ SSID : \/ {print $2}')\n\t\t\n\t\t&#91; -z \"${currrent_SSID}\" ] &amp;&amp; return 1\n\t\t\n\t\techo \"${currrent_SSID}\"; return 0\n\t}\n\n\t#test interface specified or fallback to default interface\n\tinterfaceID=${1:-$(route get 0.0.0.0 2&gt;\/dev\/null | awk '\/interface: \/ {print $2}')}\n\n\t#WIFI: key: PrivacyProxyEnabled, file: \/Library\/Preferences\/com.apple.wifi.known-networks.plist\n\t#if no error getting WiFi SSID, then we are WiFi\n\tif wifiSSID=$(getCurrentWiFiSSIDforInterface \"${interfaceID}\"); then\n\n\t\t#key name inside plist\n\t\tkeyName=\"wifi.network.ssid.${wifiSSID}\"\n\n\t\t#oddly this file is only read-able by root\n\t\tif &#91; ! -r \/Library\/Preferences\/com.apple.wifi.known-networks.plist ]; then\n\t\t\techo \"Insufficient preferences to determine WiFi state, run as root\" &gt;\/dev\/stderr\n\t\t\texit 1\n\t\tfi\n\n\t\t#get JSON version, so much easier to get around, convert date and data types to strings\n\t\twifiKnownNetworks_JSON=$(defaults export \/Library\/Preferences\/com.apple.wifi.known-networks.plist - | sed -e 's,&lt;date&gt;,&lt;string&gt;,g' -e 's,&lt;\/date&gt;,&lt;\/string&gt;,g' -e 's,&lt;data&gt;,&lt;string&gt;,g' -e 's,&lt;\/data&gt;,&lt;\/string&gt;,g' | plutil -convert json -o - -)\n\t\t\n\t\t#if there is NO entry, then it is active, it is opt-out designed\n\t\tPrivacyProxyEnabled=$(ljt \"\/wifi.network.ssid.${wifiSSID}\/PrivacyProxyEnabled\" 2&gt;\/dev\/null &lt;&lt;&lt; \"${wifiKnownNetworks_JSON}\")\n\t\t\n\t\tif &#91; \"${PrivacyProxyEnabled}\" = \"false\" ]; then\n\t\t\treturn 1\n\t\tfi\n\t#ETHERNET: key: DisablePrivateRelay, file: \/Library\/Preferences\/SystemConfiguration\/preferences.plist, \n\telse\n\t\t#get JSON, easily converts with not data or date types within\n\t\tsystemConfigPrefsJSON=$(plutil -convert json -o - \/Library\/Preferences\/SystemConfiguration\/preferences.plist)\n\t\t#get current set UUID\n\t\tcurrentSet=$(echo \"${systemConfigPrefsJSON}\" | ljt \/CurrentSet 2&gt;\/dev\/null)\n\t\t#get value for current default interface of current Location set\n\t\tDisablePrivateRelay=$(ljt \"${currentSet}\"\/Network\/Interface\/${interfaceID}\/DisablePrivateRelay  2&gt;\/dev\/null &lt;&lt;&lt; \"${systemConfigPrefsJSON}\")\n\n\t\t#if it is TRUE we are Disabled, then we are NOT ON, return fail code\n\t\tif &#91; \"${DisablePrivateRelay}\" = \"1\" ]; then\n\t\t\treturn 1\n\t\tfi\n\tfi\n)\n\n########\n# MAIN #\n########\n\nif &#91; ! -r \/Library\/Preferences\/com.apple.wifi.known-networks.plist ]; then\n\techo \"Insufficient privileges to determine WiFi state, run as root\" &gt;\/dev\/stderr\n\texit 1\nfi\n\n#use default interface if nothing is specified for argument $1\ninterface=${1:-$(route get 0.0.0.0 2&gt;\/dev\/null | awk '\/interface: \/ {print $2}')}\n\n#returns 0 if ON and 1 if OFF, you may supply an interface or it will fall back to the default\nlimitIPTracking \"${interface}\" &amp;&amp; echo \"Limit IP tracking is ON for: ${interface}\" || echo \"Limit IP tracking is OFF for: ${interface}\"\n<\/code><\/pre>\n\n\n\n<p>You can specify an interface or it will use the default interface (there&#8217;s a good one-liner to use <code>route get 0.0.0.0<\/code> to figure that out). <code>plutil<\/code> is not initially used to convert <code>com.apple.wifi.known-networks.plist<\/code> to JSON as it will fail due to XML\/plist data types like <code>&lt;date&gt;<\/code> and <code>&lt;data&gt;<\/code> that do not have JSON equivalents. First we use <code>sed<\/code> to change them to <code>&lt;string&gt;<\/code> types <em>then<\/em> <code>plutil<\/code> can convert to JSON. After that it&#8217;s a cake walk for <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/brunerd\/ljt\" target=\"_blank\">ljt<\/a> to get the value and report back. If the interface is not WiFi then <code>\/Library\/Preferences\/SystemConfiguration\/preferences.plist<\/code> has none of those conversion issues. Check out <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/limitIPTrackingStatus.sh\">limitIPTrackingStatus.sh<\/a> and the minified <a href=\"https:\/\/github.com\/brunerd\/macAdminTools\/blob\/main\/Scripts\/limitIPTrackingStatus.min.sh\">limitIPTrackingStatus.min.sh<\/a> at my GitHub. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/limitIPTrackingStatus-On.png\"><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"84\" src=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/limitIPTrackingStatus-On.png\" alt=\"\" class=\"wp-image-1229\" srcset=\"https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/limitIPTrackingStatus-On.png 476w, https:\/\/www.brunerd.com\/blog\/wp-content\/uploads\/limitIPTrackingStatus-On-300x53.png 300w\" sizes=\"auto, (max-width: 476px) 100vw, 476px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p>Thanks for reading! P.S. I won&#8217;t be at JNUC 2022, better luck next year!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you are a Mac admin you might have noticed some Apple plists are more complex than others these days. As evidenced in my post Don&#8217;t Be a Jerk, getting the status of Do Not Disturb in Big Sur was a multi-step exercise. Check out this modified code excerpt from doNotDisturb&#8216;s Big Sur handling Why [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3,19,51,53,12],"tags":[20,34,54,25,22,24],"class_list":["post-1188","post","type-post","status-publish","format-standard","hentry","category-apple","category-bash","category-json","category-ljt","category-scripting","tag-bash","tag-json","tag-ljt","tag-macos","tag-scripting","tag-shell"],"_links":{"self":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/comments?post=1188"}],"version-history":[{"count":18,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1188\/revisions"}],"predecessor-version":[{"id":1604,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/posts\/1188\/revisions\/1604"}],"wp:attachment":[{"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/media?parent=1188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/categories?post=1188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.brunerd.com\/blog\/wp-json\/wp\/v2\/tags?post=1188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}