Skip to content

Updating objects

The update command changes an existing configuration object, or a singleton, the single instance of an object type such as the system settings. It is the everyday command for adjusting a setting from the shell or a script, without re-creating the object.

An update is sent as a JMAP patch. A patch lists only the fields to change, and every field it does not mention keeps its current value, so an update never has to repeat the whole object. The patch has the shape of the update argument of a JMAP Foo/set call.

stalwart-cli update <object> [<id>] ( --field key=value... | --json '<json>' | --file <path> | --stdin )

The <object> argument is the object type name, with or without the x: prefix. The slash form is rejected, because selecting a variant is a create feature.

The <id> argument is required for a regular object. For a singleton it is optional: it defaults to singleton, and any other id is rejected. The four input forms are mutually exclusive, so exactly one of them supplies the patch.

A --field key decides how much of a property the update replaces, and it takes one of two forms:

  • Top-level property name: --field name=foo replaces the entire property.
  • JSON pointer path: --field 'aliases/2/name=foo' patches a sub-element. A JSON pointer is a /-separated path that addresses one element inside a JSON document, here the name of the alias at index 2. The first segment is matched against the schema case-insensitively and rewritten to its canonical case. Deeper segments, such as map keys, list indices and nested object properties, are passed through verbatim.

A path changes one element without sending the whole field, which matters for collections: replacing a whole set to add one entry would require knowing, and repeating, every existing entry. The following command adds one entry to a set:

Terminal window
stalwart-cli update domain b --field 'aliases/extra.example.com=true'

Setting the same path to null removes the entry from a set or map:

Terminal window
stalwart-cli update domain b --field 'aliases/extra.example.com=null'

The paths follow the patch model that JMAP itself uses. In that model, every kind of collection is encoded as a JSON object, so each element can be addressed by a key:

  • A set is encoded as a map of value -> true. Setting a value to true adds it, and setting it to null removes it.
  • An object list is encoded as a map from stringified positional indices to objects. A new index adds an object, and setting an index to null removes it.
  • A map is a plain JSON object. Setting a key’s value updates it, and setting it to null deletes it.

The parser splits each --field argument on its first =. In a path that contains a literal =, for example in a map key, that split would land inside the path. Quoting the entire key avoids this: everything between the matching quote characters becomes the key, and the first = after the closing quote separates the key from the value:

Terminal window
stalwart-cli update systemsettings --field '"proxyTrustedNetworks/127.0.0.1"=true'

A / inside a key segment raises a different problem, because / separates the segments of the path. A segment that contains one, such as a network in CIDR notation, is written with the escapes of RFC 6901, the JSON pointer specification: ~1 for / and ~0 for ~. The CLI passes the path unchanged, and the server decodes the escapes. The following command adds 10.0.0.0/8 to the trusted proxy networks:

Terminal window
stalwart-cli update systemsettings --field 'proxyTrustedNetworks/10.0.0.0~18=true'

Because a singleton has only one instance, it needs no real id. It is addressed by the literal id singleton, which is also used when the id is omitted, so the following commands are equivalent:

Terminal window
stalwart-cli update systemsettings --field defaultHostname=mail.example.com
stalwart-cli update systemsettings singleton --field defaultHostname=mail.example.com

Any other id is refused for a singleton.

The input sources are the same as for create, and each suits a different situation:

SourceWhen to use
--field key=valueQuick targeted edits from the shell
--json '<json>'A small structured patch composed inline
--file <path>A patch generated by another tool or template
--stdinA patch piped from another command

With the JSON forms, the document is the patch object sent to JMAP, not the full object. Its keys are therefore property names or JSON pointer paths, exactly as with --field, and a sub-key is set with a key that contains /:

Terminal window
stalwart-cli update domain b --json '{"description": "Renamed", "aliases/extra.example.com": true}'

Repeating the same key with --field is an error. So is supplying more than one input source.

The following commands cover the common kinds of change. A scalar field, such as a description, is changed by setting it:

Terminal window
stalwart-cli update domain b --field description='Primary domain'

A flag of a singleton is changed without an id:

Terminal window
stalwart-cli update imap --field allowPlainTextAuth=false

A structured field is replaced by passing its full JSON value:

Terminal window
stalwart-cli update domain b \
--field 'certificateManagement={"@type":"Automatic","acmeProviderId":"a"}'

An object list is addressed by index, so a new alias is added at the next available index:

Terminal window
stalwart-cli update Account c --field 'aliases/3={"name":"hello","domainId":"b"}'

An id is added to a set of ids by setting it to true:

Terminal window
stalwart-cli update Account c --field 'memberGroupIds/g1=true'

Setting the same id to null removes it again:

Terminal window
stalwart-cli update Account c --field 'memberGroupIds/g1=null'

A patch generated by another tool is read from standard input:

Terminal window
generate-domain-patch.sh | stalwart-cli update domain b --stdin

A multi-variant field holds one of several shapes, called variants, and its @type names the one in use. certificateManagement on a domain is an example, with a Manual and an Automatic variant. Changing the variant requires replacing the entire sub-object in one patch, because JMAP cannot mix @type/... JSON pointer keys with changes to sibling properties when the type itself changes. The new variant’s full body is therefore sent at once:

Terminal window
stalwart-cli update domain b \
--field 'certificateManagement={"@type":"Automatic","acmeProviderId":"a"}'

The CLI does not validate variant changes, so errors surface only when the server checks the patch. A complex variant transition is therefore easier to get right in a JSON file built by a configuration tool.

On success, the CLI prints a confirmation line:

Updated Domain b

Some fields are set by the server rather than by the caller, and the server may return them after an update, mostly when it recomputes them. The CLI prints such fields below the confirmation as key: <value> lines.