Filed as https://github.com/netxms/netxms/issues/3480 for design discussion.
Option 3 is the right shape, but one thing needs settling before you build on it: user custom attributes will not work as a backing store for ordinary users. CMD_UPDATE_USER is handled by ClientSession::updateUser() and requires SYSTEM_ACCESS_MANAGE_USERS, with no exemption for modifying your own account — password change and 2FA binding both special-case userId == m_userId, custom attributes do not. A non-admin operator saving their own date format gets access denied.
So the per-user store needs a server-side change either way: a self-update path restricted to own custom attributes, or a separate per-user key/value store with its own command and access check. That choice determines the client API, so it should come before more work on the merge logic.
Beyond that, option 3 needs an explicit rule for which preferences are machine-local and which are per-user. Without one, every preference added later is a judgement call and the split rots — your HTTP_* and window geometry cases are exactly what such a rule has to answer.
Your points 3 and 4 are a separate problem, worth splitting off: the RWT store sits in the servlet container state directory, so it is lost on container restart and on .war update. Persisting that directory, or moving the store out of it, fixes preference loss in the web client on its own, with none of the per-user work — and it covers theme files, map tile cache and MIB downloads at the same time.