Proposal: optional_uninstalls

153 views
Skip to first unread message

Gregory Neagle

unread,
Aug 20, 2026, 1:17:37 PM (5 days ago) Aug 20
to munki-dev
In the past I’ve made some proposals for marking items as being uninstallable only; the idea being that these are items you’ve offered as optional_installs. You now don’t want any new installs, but would like to allow users to (optionally) remove the item if they’d like. My original thoughts were to make it a new pkginfo key. In any case, I never implemented that.

I was thinking about this problem again today after looking at all the older software we still have in optional_installs, and realized that this really should be a new manifest section: “optional_uninstalls”. So I chatted with Claude Code:

> Help me add a feature. Munki should support a new manifest key named "optional_uninstalls". Items listed there are only          

  available for _uninstall_ and only appear in Managed Software Center if they are currently installed. This allows admins to      

  "phase out" software items -- they can move items from "optional_installs" to "optional_uninstalls" to prevent any new           

  installations of an item, but also allowing users to remove the items from their Macs if and when they want.


After some thinking, and a correction when it wanted to make changes to the Python files in munkilib, it came up with this plan:


 Plan: Add optional_uninstalls manifest key to Munki (Swift only)


 Context


 Admins need a way to "phase out" software: prevent new installs while still letting users self-remove copies already on their

 Macs. The new optional_uninstalls manifest key fills that gap. Items listed there:

 - Only appear in Managed Software Center if they are currently installed

 - Only offer Uninstall (never Install)

 - Behave like optional_installs in every other way (self-serve via SelfServeManifest, catalogs, etc.)


 ---

 Approach


 1. Separate key in InstallInfo.plistoptional_uninstalls stored as its own top-level array. Keeps semantics clear and doesn't

 touch the existing flow.

 2. New processOptionalUninstall() function — mirrors processOptionalInstall() but only adds an item if it is currently installed,

 and skips install-only paths (precache, disk-space note for not-installed, etc.).

 3. MSC merges both listsgetOptionalInstallItems() reads both arrays from InstallInfo.plist, tagging optional_uninstalls items

 with uninstall_only = true. Items tagged uninstall_only that are not installed are dropped at load time.

 4. Status / button logic — for uninstall_only items the not-installedinstall-requested transition in update_status() is

 blocked.

 5. mutuallyExclusiveSectionsoptional_uninstalls added so an item can't simultaneously be in optional_installs.


 ---

 Files to change


 code/cli/munki/shared/updatecheck/analyze.swift


 - New async function processOptionalUninstall(_:catalogList:installInfo:):

   - Mirrors processOptionalInstall() structure.

   - After alreadyProcessed() check, also checks optional_uninstalls list for a duplicate name.

   - Calls someVersionInstalled() — if not installed, logs and returns without adding anything.

   - Skips the unusedSoftwareShouldBeRemoved / precache / disk-space-for-not-installed blocks.

   - Builds the same processedItem dict (name, display_name, description, version_to_install, installed=true, uninstallable,

 etc.).

   - Appends to installInfo["optional_uninstalls"] instead of optional_installs.


 code/cli/munki/shared/updatecheck/manifests.swift


 - In processManifest(_:forKey:installInfo:parentCatalogs:manifestName:) (~line 386), inside the item loop: add if key ==

 "optional_uninstalls" { _ = await processOptionalUninstall(item, catalogList: catalogList, installInfo: &installInfo) }.


 code/cli/munki/shared/updatecheck/updatecheck.swift


 - installInfo init dict (~line 297): add "optional_uninstalls": [PlistDict]().

 - processLocalOnlyManifest() key list (~line 132): add "optional_uninstalls" to the iterated key array.

 - After processManifest(... "optional_installs" ...) (~line 381): add a parallel call for "optional_uninstalls".

 - processSelfServeManifest() (~line 213 — the will_be_removed update loop): add an identical loop over

 installInfo["optional_uninstalls"] items to set will_be_removed when they appear in removals.


 code/cli/munki/shared/installer/installer.swift


 - Post-install status update (~line 735): add a parallel loop for installInfo["optional_uninstalls"] items. For each removal

 result whose name matches, update removal_error / installed flags identically to the existing optional_installs loop. (No

 install-result path needed since these items can never be installed via this key.)


 code/apps/Managed Software Center/Managed Software Center/MunkiItems.swift


 - optionalInstallsExist() (~line 1152): also check optional_uninstalls array non-empty (so the sidebar appears when only

 phase-out items are present and installed — though in practice they'd be filtered by install state later, so it's cleaner to

 check raw count here and let getOptionalInstallItems() filter).

 - getOptionalInstallItems() (~line 1157):

   - Also read cachedInstallInfo()["optional_uninstalls"] as? [[String:Any]].

   - Filter to items where installed == true.

   - Set uninstall_only = true on each before wrapping in OptionalItem.

   - Append to the combined optional_install_items array before caching.

 - OptionalItem._get_status() (~line 743): if uninstall_only == true and installed == false, return "" early (the item will be

 excluded). The installed branch already produces correct removal-oriented statuses with no changes needed.

 - OptionalItem.update_status() (~line 941): in the "not-installed" case, add a guard — if uninstall_only == true, do nothing and

 return false (no install transition allowed).


 code/cli/munki/manifestutil/MUmanifestEditing.swift


 - validPkgSections (~line 77): add "optional_uninstalls".

 - mutuallyExclusiveSections (~line 84): add "optional_uninstalls" so it can't coexist with managed_installs, managed_uninstalls,

 or optional_installs for the same item.


 code/cli/munki/manifestutil/MUrunInteractive.swift


 - sections array (~line 56): add "optional_uninstalls" for tab completion.


 code/cli/munki/repoclean/repoclean.swift


 - Lines 145 and 156: add "optional_uninstalls" to the manifest section key arrays that repoclean scans.


 ---

 Verification


 1. Existing tests — run analyzeTests.swift and the full test suite to confirm no regressions.

 2. Manual end-to-end:

   - Item in optional_installs — confirm unchanged behavior.

   - Item moved to optional_uninstalls on a machine where it is not installed → should not appear in MSC at all.

   - Same item on a machine where it is installed → appears in MSC with only a "Remove" button; clicking it writes to

 SelfServeManifest; next managedsoftwareupdate run removes it; item disappears from MSC afterward.

 3. manifestutilmanifestutil --add-pkg <item> --section optional_uninstalls <manifest> accepted; same item rejected if already

 in optional_installs.

 4. repoclean — runs without error against a repo whose manifests contain optional_uninstalls.


This seems a reasonable plan. Does this feature sound interesting/reasonable? Would you use it? (Pretty sure I would)

This is something I’ve wanted for a while and just never got around to thinking about it hard enough to start implementing it. AI code assist now makes it much easier to get started with actually implementing it...


-Greg

Gregory Neagle

unread,
Aug 20, 2026, 4:13:05 PM (5 days ago) Aug 20
to munki-dev
There was a suggestion on MacAdmins Slack that instead of adding a new manifest key, instead make this behavior implicit:

If a user installs an optional_installs item, the item name is added to managed_installs in the SelfServeManifest on the local machine.

Currently, any item in the SelfServeManifest that is not in optional_installs (in any admin=managed manifest) is silently ignored — this is to prevent clever users from just adding items they want to the SelfServeManifest.

The suggestion is that if an item is in managed_installs in the SelfServeManifest, and is _not_ in optional_installs, if we can find the item in an available catalog, we offer it in Managed Software Center for uninstall only.

Another way to look at this is that items removed from optional_installs (and are not in an admin-managed manifest’s managed_installs or managed_uninstalls) are treated as a sort of “virtual” optional_uninstall item. It would still appear in MSC.app with the option to remove it. If the item is then removed, it would be removed and would not be displayed again in MSC.app. If the item is removed from available catalogs, it would disappear from MSC.app (whether it is installed or not).

This seems like the behavior many admins would want much of the time, and requires no manifest editing at all. But it would be new implicit behavior that might take admins by surprise (and that some admins might not want for some reason).

So looking for reactions to the original proposal, and the Slack suggestion (thanks Melvin Peeters).

-Greg

--
Find related discussion groups here:
https://github.com/munki/munki/wiki/Discussion-Group
---
You received this message because you are subscribed to the Google Groups "munki-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to munki-dev+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/munki-dev/E27F9FDC-E361-4939-A469-3DD713E4FECB%40mac.com.

Nick McSpadden

unread,
Aug 20, 2026, 5:57:32 PM (5 days ago) Aug 20
to munk...@googlegroups.com
This sounds really cool, and I like the idea, but my one concern is this:


>   It would still appear in MSC.app with the option to remove it. If the item is then removed, it would be removed and would not be displayed again in MSC.app

I would like to have some very explicit warning in MSC (maybe in red text or something) that clearly lays out that removing this item means you won't be able to find it again. 



--
--
Nick McSpadden
nmcsp...@gmail.com

Gregory Neagle

unread,
Aug 20, 2026, 7:35:19 PM (5 days ago) Aug 20
to munki-dev

Allister Banks

unread,
Aug 20, 2026, 7:38:30 PM (5 days ago) Aug 20
to munk...@googlegroups.com, munk...@googlegroups.com
On Aug 21, 2026, at 6:57 AM, Nick McSpadden <nmcsp...@gmail.com> wrote:

I would like to have some very explicit warning in MSC (maybe in red text or something) that clearly lays out that removing this item means you won't be able to find it again. 

I believe the ‘preuninstall_alert’ functionality that can be leveraged in the MSC GUI would be good to adopt if this requires extra attention, although I admit this adds to the coordination and cognitive load/need to be aware of that knob/feature of pkginfo files when orchestrating the change via the manifest.

In contrast to the original post that started this mail thread I tend to agree with the Slack-sourced idea and think the lack of knobs to bring about the feature is better. I feel this even in the face of the potentially ‘magic’-seeming behavior where admins adopting a previous environment (or just forgot they used to offer the software optionally) are unaware of this new intended behavior.
Allister

Nick McSpadden

unread,
Aug 20, 2026, 7:53:01 PM (5 days ago) Aug 20
to munk...@googlegroups.com
It certainly could, but I would be of the opinion that it should likely be a default warning instead of depending on the admins to do this. Feels a teensy bit like an unnecessary footgun, but I don't feel super strongly about it.

Gregory Neagle

unread,
Aug 20, 2026, 8:05:54 PM (5 days ago) Aug 20
to munki-dev
My concern here is coming to a consensus on what an appropriate, clear, and brief message should be, and then translating that into the 11 other languages currently supported by MSC.app.

Not really understanding the “footgun” aspect. If a user uses this feature to uninstall an item, the admin’s expectation is that it cannot (trivially/easily) be re-installed. That’s the _point_ here. The issue is: how do you make that clear to the user before they click uninstall? There’s no real _damage_ here implied by a “footgun”.

-Greg

Nick McSpadden

unread,
Aug 20, 2026, 8:14:28 PM (5 days ago) Aug 20
to munk...@googlegroups.com
My concern is that users, well known for experiencing random bouts of insanity, may see an option to remove software that they are currently having a problem with, and think "oh I can just reinstall it" as a solution. If there isn't a clear indication that the software is remove-only, an inquisitive user remove the software and discover that they can't get it back. I'm less concerned about the admin experience here and more concerned about a user ending up in an unexpected situation that then generates a support ticket/request for help. A warning "this software won't come back if you remove it" (not that phrase exactly) would help mitigate any lack of clarity about the implications of their choice to remove it.

Gregory Neagle

unread,
Aug 20, 2026, 8:21:15 PM (5 days ago) Aug 20
to munki-dev
So all we are really discussing is “should the local admin provide that message, which can be customized with local/relevant support info (phone/email/web)” or should the developers of Munki provide a generic message? And if the latter, should that be “smart” enough to not display if the admin does decide to implement a preuninstall_alert?

-Greg

Gregory Neagle

unread,
Aug 20, 2026, 8:25:35 PM (5 days ago) Aug 20
to munki-dev
Taking that thought a bit further, users are most likely going to want to know _why_ the software won’t be available for re-install, and that would be even harder to capture generically.

I’d probably update the _description_ for the item, doing something like:

“Maya 20XX is no longer supported. If you still need to use Maya, please install Maya 2026. You may remove this version at your convenience”.

-Greg

Gregory Neagle

unread,
Aug 20, 2026, 8:36:19 PM (5 days ago) Aug 20
to munki-dev
And now I take the thought even _further_.

A major reason I want this functionality is to actually _encourage_ users to remove old/abandoned software we really don’t support any longer, but I don’t feel comfortable making them managed_uninstalls.

The proposed implementation would make it possible for users to remove the software (while also not allowing new installs or reinstalls), but would do nothing towards _encouraging_ people to remove the items.

To do that, these items would likely have to be called out separately/differently in MSC.app. Perhaps they need their own “section” or virtual Category, for example: “Deprecated software”. If it’s clear that this software is deprecated/unsupported/EOL then it should not be a surprise that once removed, there’s no clear option to reinstall it.

Still thinking...

-Greg

Gregory Neagle

unread,
Aug 20, 2026, 8:52:35 PM (5 days ago) Aug 20
to munki-dev
A mockup of one possibilty. This is similar in concept to “Other updates”, in which there are items that are installed but not in the SelfServeManifest, and there’s a newer version in optional_installs.

PastedGraphic-1.png

Mike Solin

unread,
Aug 20, 2026, 9:48:56 PM (5 days ago) Aug 20
to munk...@googlegroups.com
Agreed - I think putting it in the Updates tab makes the most sense.

Would this display a notification to the user, or would users find this organically when visiting MSC for something else, like a pending update?



Gregory Neagle

unread,
Aug 20, 2026, 9:51:06 PM (5 days ago) Aug 20
to munk...@googlegroups.com, munk...@googlegroups.com
No notifications, just like there are none for “Other updates”. 

Sent from my iPhone

On Aug 20, 2026, at 6:49 PM, Mike Solin <mi...@mikesolin.com> wrote:


Agreed - I think putting it in the Updates tab makes the most sense.

Would this display a notification to the user, or would users find this organically when visiting MSC for something else, like a pending update?



On Thu, Aug 20, 2026 at 8:52 PM 'Gregory Neagle' via munki-dev <munk...@googlegroups.com> wrote:
A mockup of one possibilty. This is similar in concept to “Other updates”, in which there are items that are installed but not in the SelfServeManifest, and there’s a newer version in optional_installs.

Jordan Calhoun

unread,
Aug 21, 2026, 10:04:13 AM (4 days ago) Aug 21
to munki-dev
*thinking outloud*

No notifications and displayed in Updates makes sense to me.  

A sidebar item that shows up only when there is deprecated software would also make sense to me; that way you can have a count on sidebar item similar to updates; it would also allow for clear separation, better discoverability, future features to be added to this without the "updates" section getting convoluted over time, and makes it very clear to users this is different than an update.

Future feature of working this into "update_for" could be a really nice UX flow that makes it clear to the user when an app is no longer supported and there is a replacement for it


Gregory Neagle

unread,
Aug 21, 2026, 11:11:37 AM (4 days ago) Aug 21
to munk...@googlegroups.com
> Future feature of working this into "update_for" could be a really nice UX flow that makes it clear to the user when an app is no longer supported and there is a replacement for it

I’m thinking far simpler: the admin would change the description:

Foo is no longer recommended or supported. Please consider removing it from your Mac. Possible replacement applications include Bar and Baz. Contact Help Desk if you need additional information.

You could even make “Bar” and “Baz” munki:// links to their product “pages”.

-Greg

Gregory Neagle

unread,
Aug 21, 2026, 8:13:40 PM (4 days ago) Aug 21
to munki-dev
Working today with Claude, here’s where I am.

I opted for an explicit “optional_uninstalls” key. As you’ll see below, it might make sense to change that key name to “suggested_uninstalls”.

For this demo, I added “VirtualBuddy” to optional_uninstalls for my machine’s manifest:

PastedGraphic-1.png

The new “Suggested removals” section acts very much like the “Other available updates” section that appears when there are optional installs, currently installed, but not in the SelfServeManifest (perhaps the items were manually installed, or migrated from another Mac). Clicking “Remove” on an item in this section adds the item to managed_uninstalls in the SelfServeManifest and triggers an update check. Once the update check is complete, the item appears under “Pending updates”:

PastedGraphic-2.png

Note the Cancel button — the user can change their mind about uninstalling by clicking “Cancel”. This also mirrors the behavior around “Additional updates”. If the user clicks the cancel button, the item is removed from managed_uninstalls in the SelfServeManifest and triggers an update check, and the item “returns” to the “Suggested removals” section.

Clicking the big blue Update button would then trigger the actual removal of the item. 

Gregory Neagle

unread,
Aug 21, 2026, 8:20:07 PM (4 days ago) Aug 21
to munki-dev
Code is in the “optional_uninstalls” branch.

-Greg

On Aug 21, 2026, at 5:13 PM, 'Gregory Neagle' via munki-dev <munk...@googlegroups.com> wrote:

Working today with Claude, here’s where I am.

I opted for an explicit “optional_uninstalls” key. As you’ll see below, it might make sense to change that key name to “suggested_uninstalls”.

For this demo, I added “VirtualBuddy” to optional_uninstalls for my machine’s manifest:

<PastedGraphic-1.png>

The new “Suggested removals” section acts very much like the “Other available updates” section that appears when there are optional installs, currently installed, but not in the SelfServeManifest (perhaps the items were manually installed, or migrated from another Mac). Clicking “Remove” on an item in this section adds the item to managed_uninstalls in the SelfServeManifest and triggers an update check. Once the update check is complete, the item appears under “Pending updates”:

Reply all
Reply to author
Forward
0 new messages