niwaniwa0.1.2
saru-id/niwa
niwaniwa

Manage system settings

Declare preference domains, the Dock, and the Finder, and know which process restarts when a key changes.

When to use this

Use this guide when a macOS setting should survive a rebuild, or should be the same on two machines. niwa reads and writes preference plists directly, so anything the preference system stores can be declared.

Some settings are not preferences. An application that keeps its state somewhere else is out of reach here.

What must already be true

  • A config repo exists, written by niwa init.
  • You know the domain and the key. If you set it by hand first, niwa pull offers you the line.
  • Domains under /Library/Preferences need administrator rights. Apply names those steps before it asks you to confirm, and apply --no-privileged skips them whole.

Declare a domain

niwa.defaults takes a domain, a table of keys, and an optional table of options.

luau
niwa.defaults("com.apple.WindowManager", {
  StandardHideWidgets = true,
  EnableStandardClickToShowDesktop = false,
}, { restart = "Dock" })

niwa.defaults("NSGlobalDomain", {
  KeyRepeat = 2,
  InitialKeyRepeat = 15,
  ApplePressAndHoldEnabled = false,
})

Each key is its own resource. The first key above has the identity defaults:com.apple.WindowManager:StandardHideWidgets. Two modules can declare different keys of one domain and never meet. Two modules declaring the same key with different values is a conflict, and check names both source locations.

Where the keys land

A bare domain is a reverse-DNS name and lives in ~/Library/Preferences/<domain>.plist. NSGlobalDomain is the one exception: it is .GlobalPreferences.plist in the same directory.

An absolute domain must start with /Library/Preferences/, and niwa refuses any other absolute path when the config loads. A key that a configuration profile manages is refused too: the profile is the owner, and the error names it.

The Dock and the Finder

Two things people change often have sugar. It lowers to the same identities as the generic form, so a host file can override one key of it and conflict detection sees through the spelling.

luau
niwa.dock {
  autohide = true,
  tilesize = 48,
  apps = {},
  minimize_effect = "scale",
}

niwa.finder {
  show_hidden = true,
  default_view = "list",
  path_in_title = true,
}

apps accepts the empty list and nothing else in this build: a populated Dock stores tile dictionaries that this version does not build. default_view takes list, icon, column, or gallery, and niwa writes the four-character code the plist expects.

What restarts

restart names a process to stop once the writes have landed. The Dock sugar and the Finder sugar name theirs for you. Restarts run at the end of the pass, one per named process, so five keys that name the Dock restart it once. A run that wrote any preference invalidates the preference cache first, so no cached copy can undo the write.

Read the plan first

 shell ─────────────────────────────
+ ~/.zshrc
 desktop ───────────────────────────
~ com.apple.dock autohide   false → true
~ 3 checked · 2 would change
niwa plan · tests/snapshots/snapshots__plan_pending_color.snap

The line names the key, then the change: the value on the machine, then the value you declared. Group headers are your modules. undo reverses a preference like any other change: it puts the previous value back, and removes a key that was not there before.

Next

View as markdown

↑↓ navigate · enter opens · esc closes