macOS Open Directory: Does ODNodeCreateRecord cleanup preserve concurrent records?

We are designing a local, non-login service account provisioning process on macOS using the public Open Directory API.

The documentation for ODNodeCreateRecord() states that if setting an initial attribute fails, the record is deleted and an error is returned.

We need to ensure that a failed creation attempt cannot delete or modify a pre-existing record, including during concurrent record creation or replacement.

Questions:

  1. When attribute initialization fails, is the internal cleanup guaranteed to target only the record instance created by that specific API call, rather than another record with the same name?
  2. Is this cleanup bound to a unique record identity or UUID, including if another process concurrently creates, removes, or replaces a record with the same name?
  3. Does the public API provide an atomic create-only guarantee that prevents changes to an existing record?
  4. If these guarantees are not provided, what supported API or provisioning approach should be used to avoid affecting pre-existing local Directory Services records?

Our current plan uses the local /Local/Default Directory Services node for a dedicated service account, administrative change serialization, pre-creation collision checks, and post-operation identity verification.
We understand that these checks do not themselves prove atomicity or race-free cleanup.

We would appreciate clarification of the supported API guarantees, specifically for macOS 15.

Thank you.

Answered by DTS Engineer in 915666022

The Open Directory framework doesn’t provide any atomicity guarantees here. Rather, it passes the entire operation off to the underlying OD module. So, any guarantees are going to come from that module.

It seems that you’re primarily concerned with the local node, as run by the PlistFile module. AFAIK that module processes commands atomically, and I expect that lots of folks end up relying on that, but I’m not sure that it’s documented anywhere.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

I don’t understand your actual concern here. It sounds like you’re worried about this sequence:

  1. Your process creates account A.
  2. Simultaneously, some other process creates account A.
  3. One of these will ‘win’, in which case the other will lose. If the other process wins, the system will then clean up the wreckage of its creation attempt for your record.
  4. And you’re concerned that that cleanup might destroy some other record, possibly the one created be the other process.

Is that what you’re worried about here?

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you, Quinn. Yes, that is exactly the concern.

More specifically, we want to ensure that a failed ODNodeCreateRecord() attempt cannot delete or modify a record created by another process, even when both operations involve the same record name.

We are also concerned about a related race where a record is removed and recreated under the same name while creation or attribute-initialization cleanup is in progress.

Could you clarify the following for the local /Local/Default node on macOS 15?

  1. Is cleanup after an attribute-initialization failure guaranteed to affect only the specific record instance created by that API call, rather than another record with the same name?
  2. Is that cleanup tied to an immutable record identity, such as a UUID, even if a same-named record is concurrently removed or replaced?
  3. Does ODNodeCreateRecord() provide atomic create-only semantics that prevent an existing record from being modified or deleted by a failed competing creation?
  4. If these guarantees are not provided, is there a supported API or provisioning approach that prevents accidental interference with pre-existing local Directory Services records?

Our goal is to provision a dedicated non-login service account without risking changes to any existing account.

We understand that pre-creation collision checks, administrative serialization, and post-operation identity verification do not independently establish these guarantees.

We are looking for the supported behavior rather than relying on assumptions about the current implementation.

Thank you for your guidance.

The Open Directory framework doesn’t provide any atomicity guarantees here. Rather, it passes the entire operation off to the underlying OD module. So, any guarantees are going to come from that module.

It seems that you’re primarily concerned with the local node, as run by the PlistFile module. AFAIK that module processes commands atomically, and I expect that lots of folks end up relying on that, but I’m not sure that it’s documented anywhere.

Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"