Skip to content

Base

ConfigBase::DictFieldProxy::assign_if_changed

Takes a value and assigns it to the dict only if that value is different. Will avoid dirtying the config if the assignement isn't changing anything

Declaration

template <typename T>
void assign_if_changed(T value) {

Parameters

  • value — This will be assigned to the dict if it has changed

Returns

ConfigBase::DictFieldProxy::dict

Returns a const pointer to the dict if one exists at the given location, nullptr otherwise. (You typically don't need to use this but can rather just use [] to descend into the dict).

Declaration

const config::dict* dict() const { return get_clean<config::dict>(); }

Parameters

This endpoint takes no inputs.

Returns

  • config::dict* — Returned pointer to the dict if one exists

ConfigBase::DictFieldProxy::emplace

Emplaces a value at the current location. As with assignment, this creates dicts as needed along the keys to reach the target. The existing value (if present) is destroyed to make room for the new one.

Declaration

template <
        typename T,
        typename... Args,
        typename = std::enable_if_t<
                is_one_of<T, config::set, config::dict, int64_t, std::string>>>
T& emplace(Args&&... args) {

Parameters

  • args — Value to be emplaced at current location

Returns

  • T& — Returns a reference to the templated type

ConfigBase::DictFieldProxy::erase

Removes the value at the current location, regardless of what it currently is. This does nothing if the current location does not have a value.

Declaration

void erase() {

Parameters

This endpoint takes no inputs.

Returns

ConfigBase::DictFieldProxy::exists

Returns true if there is a value at the current key. If a template type T is given, it only returns true if that value also is a T.

Declaration

template <typename T = dict_value, typename = std::enable_if_t<is_dict_value<T>>>
bool exists() const {

Parameters

This endpoint takes no inputs.

Returns

  • bool — True if there is a value at the current key

ConfigBase::DictFieldProxy::exists

Alias for exists<T>()

Declaration

template <typename T>
bool is() const {

Parameters

This endpoint takes no inputs.

Returns

  • bool — True if there is a value at the current key

ConfigBase::DictFieldProxy::get_clean

Same as above get_clean_pair() but just gives back the value, not the key

Declaration

template <typename T = dict_value, typename = std::enable_if_t<is_dict_value<T>>>
const T* get_clean() const {

Parameters

This endpoint takes no inputs.

Returns

  • const T* — Value

ConfigBase::DictFieldProxy::get_clean_pair

See if we can find the key without needing to create anything, so that we can attempt to access values without mutating anything (which allows, among other things, for assigning of the existing value to not dirty anything). Returns nullptrs if the value or something along its path would need to be created, or has the wrong type; otherwise a const pointer to the key and the value. The templated type, if provided, can be one of the types a dict_value can hold to also check that the returned value has a particular type; if omitted you get back the dict_value pointer itself. If the field exists but is not the requested T type, you get back the key string pointer with a nullptr value.

Declaration

template <typename T = dict_value, typename = std::enable_if_t<is_dict_value<T>>>
std::pair<const std::string*, const T*> get_clean_pair() const {

Parameters

This endpoint takes no inputs.

Returns

  • const std::string* — Key
  • const T* — Value

ConfigBase::DictFieldProxy::get_dirty

Returns a lvalue reference to the value, stomping its way through the dict as it goes to create subdicts as needed to reach the target value. If given a template type then we also cast the final dict_value variant into the given type (and replace if with a default-constructed value if it has the wrong type) then return a reference to that.

Declaration

template <typename T = dict_value, typename = std::enable_if_t<is_dict_value<T>>>
T& get_dirty() {

Parameters

This endpoint takes no inputs.

Returns

  • T& — Value

ConfigBase::DictFieldProxy::insert_if_missing

Takes a value and assigns it to the dict if it does not exist

Declaration

void insert_if_missing(config::scalar&& value) {

Parameters

  • value — This will be assigned to the dict if it is missing

Returns

ConfigBase::DictFieldProxy::integer

Returns a const pointer to the integer if one exists at the given location, nullptr otherwise.

Declaration

const int64_t* integer() const { return get_clean<int64_t>(); }

Parameters

This endpoint takes no inputs.

Returns

  • int64_t* — Pointer to the integer if one exists

ConfigBase::DictFieldProxy::integer_or

Returns the value as an integer or a fallback if the value doesn't exist (or isn't an integer).

Declaration

int64_t integer_or(int64_t fallback) const {

Parameters

  • fallback — this value will be returned if it the requested value doesn't exist

Returns

  • int64_t — Returned Integer

ConfigBase::DictFieldProxy::key

Returns a pointer to the (deepest level) key for this dict pair if a pair exists at the given location, nullptr otherwise. This allows a caller to get a reference to the actual key, rather than an ephemeral copy of the current key value.

Declaration

const std::string* key() const { return get_clean_pair().first; }

Parameters

This endpoint takes no inputs.

Returns

  • std::string* — Returns a pointer to the key if the pair exists

ConfigBase::DictFieldProxy::operator=(config::set)

Replaces the current value with the given set. This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path.

Declaration

void operator=(config::set value) { assign_if_changed(std::move(value)); }

Parameters

  • value — replaces current value with given set

Returns

ConfigBase::DictFieldProxy::operator=(config::set)

Replaces the current value with the given dict. This often isn't needed because of how other assignment operations work This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path.

Declaration

void operator=(config::dict value) { assign_if_changed(std::move(value)); }

Parameters

  • value — replaces current value with given dict

Returns

ConfigBase::DictFieldProxy::operator=(int64_t)

Replaces the current value with the given integer. This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path.

Declaration

void operator=(int64_t value) { assign_if_changed(value); }

Parameters

  • value — replaces current value with given integer

Returns

ConfigBase::DictFieldProxy::operator=(std::string&&)

Replaces the current value with the given string. This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path.

Declaration

void operator=(std::string&& value) { assign_if_changed(std::move(value)); }

Parameters

  • value — replaces current value with given string

Returns

ConfigBase::DictFieldProxy::operator=(std::string_view)

Replaces the current value with the given string_view. This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path (this makes a copy).

Declaration

void operator=(std::string_view value) { *this = std::string{value}; }

Parameters

  • value — replaces current value with given string view

Returns

ConfigBase::DictFieldProxy::operator=(ustring_view)

Replaces the current value with the given ustring_view. This also auto-vivifies any intermediate dicts needed to reach the given key, including replacing non-dict values if they currently exist along the path (this makes a copy).

Declaration

void operator=(ustring_view value) {

Parameters

  • value — replaces current value with given ustring_view

Same as above, but takes a ustring_view

Returns

ConfigBase::DictFieldProxy::operator[]&

Descends into a dict, returning a copied proxy object for the path to the requested field. Nothing is created by doing this unless you actually assign to a value.

Declaration

DictFieldProxy operator[](std::string subkey) const& {

Parameters

  • subkey — searches through the dict this requested field

Returns

  • DictFieldProxy — Returns a copied proxy object

ConfigBase::DictFieldProxy::operator[]&&

Same as above operator[]&, but when called on an rvalue reference we just mutate the current proxy to the new dict path.

Declaration

DictFieldProxy&& operator[](std::string subkey) && {

Parameters

  • subkey — searches through the dict this requested field

Returns

  • DictFieldProxy&& — Mutate the current proxy to the new dict path

ConfigBase::DictFieldProxy::set

Returns a const pointer to the set if one exists at the given location, nullptr otherwise.

Declaration

const config::set* set() const { return get_clean<config::set>(); }

Parameters

This endpoint takes no inputs.

Returns

  • config::set* — Returned pointer to the set if one exists

ConfigBase::DictFieldProxy::set_erase(int64_t)

Removes a value from the set at the current location. If the current value does not exist then nothing happens. If it does exist, but is not a set, it will be replaced with an empty set. Otherwise the given value will be removed from the set, if present.

Declaration

void set_erase(int64_t value) { set_erase_impl(scalar{value}); }

Parameters

  • value — The value to be set

Returns

ConfigBase::DictFieldProxy::set_erase(std::string_view)

Removes a value from the set at the current location. If the current value does not exist then nothing happens. If it does exist, but is not a set, it will be replaced with an empty set. Otherwise the given value will be removed from the set, if present.

Declaration

void set_erase(std::string_view value) {

Parameters

  • value — The value to be set

Returns

ConfigBase::DictFieldProxy::set_erase_impl

Erases from the dict

Declaration

void set_erase_impl(const config::scalar& value) {

Parameters

  • value — This will be deleted from the dict

Returns

ConfigBase::DictFieldProxy::set_insert(int64_t)

Adds a value to the set at the current location. If the current value is not a set or does not exist then dicts will be created to reach it and a new set will be created.

Declaration

void set_insert(int64_t value) { insert_if_missing(config::scalar{value}); }

Parameters

  • value — The value to be set

Returns

ConfigBase::DictFieldProxy::set_insert(std::string)

Adds a value to the set at the current location. If the current value is not a set or does not exist then dicts will be created to reach it and a new set will be created.

Declaration

void set_insert(std::string_view value) {

Parameters

  • value — The value to be set

Returns

ConfigBase::DictFieldProxy::string

Returns a const pointer to the string if one exists at the given location, nullptr otherwise.

Declaration

const std::string* string() const { return get_clean<std::string>(); }

Parameters

This endpoint takes no inputs.

Returns

  • std::string* — Returns a pointer to the string if one exists

ConfigBase::DictFieldProxy::string_or

Returns a copy of the value as a string, if it exists and is a string; returns fallback otherwise.

Declaration

std::string string_or(std::string fallback) const {

Parameters

  • fallback — this value will be returned if it the requested value doesn't exist

Returns

  • std::string — Returned string

ConfigBase::DictFieldProxy::string_view_or

returns the value as a string_view or a fallback if the value doesn't exist (or isn't a string). The returned view is directly into the value (or fallback) and so mustn't be used beyond the validity of either.

Declaration

std::string_view string_view_or(std::string_view fallback) const {

Parameters

  • fallback — this value will be returned if it the requested value doesn't exist

Returns

  • std::string_view — Returned string view

ConfigBase::DictFieldProxy::uview

Returns the value as a ustring_view, if it exists and is a string; nullopt otherwise.

Declaration

std::optional<ustring_view> uview() const {

Parameters

This endpoint takes no inputs.

Returns

  • std::optional<ustring_view> — Returns a value as a view if it exists

ConfigBase::DictFieldRoot::operator[]

Access a dict element. This returns a proxy object for accessing the value, but does not auto-vivify the path (unless/until you assign to it).

Declaration

DictFieldProxy operator[](std::string key) const& {

Parameters

  • key — Access a dict element with this key

Returns

  • DictFieldProxy — Returns a proxy object for accessing the value

ConfigBase::_merge

Internal implementation of merge. This takes all of the messages pulled down from the server and does whatever is necessary to merge (or replace) the current values.

Values are pairs of the message hash (as provided by the server) and the raw message body.

After this call the caller should check needs_push() to see if the data on hand was updated and needs to be pushed to the server again (for example, because the data contained conflicts that required another update to resolve).

Returns the number of the given config messages that were successfully parsed.

Will throw on serious error (i.e. if neither the current nor any of the given configs are parseable). This should not happen (the current config, at least, should always be re-parseable).

Declaration

std::vector<std::string> _merge(
        const std::vector<std::pair<std::string, ustring_view>>& configs);

Parameters

  • configs — vector of pairs containing the message hash and the raw message body

Returns

  • vector of successfully parsed hashes. Note that this does not mean the hash was recent or that it changed the config, merely that the returned hash was properly parsed and processed as a config message, even if it was too old to be useful (or was already known to be included). The hashes will be in the same order as in the input vector.

ConfigBase::add_key

Encryption key methods. For classes that have a single, static key (such as user profile storage types) these methods typically don't need to be used: the subclass calls them automatically.

Adds an encryption/decryption key, without removing existing keys. They key must be exactly 32 bytes long. The newly added key becomes the highest priority key (unless the high_priority argument is set to false' see below): it will be used for encryption of config pushes after the call, and will be tried first when decrypting, followed by keys present (if any) before this call. If the given key is already present in the key list then this call moves it to the front of the list (if not already at the front).

If the high_priority argument is specified and false, then the key is added to the end of the key list instead of the beginning: that is, it will not replace the current highest-priority key used for encryption, but will still be usable for decryption of new incoming messages (after trying keys present before the call). If the key already exists then nothing happens with high_priority=false (in particular, it is not repositioned, in contrast to high_priority=true behaviour).

Will throw a std::invalid_argument if the key is not 32 bytes.

Declaration

void add_key(ustring_view key, bool high_priority = true, bool dirty_config = false);

Parameters

  • ustring_view key — 32 byte binary key
  • high_priority — Whether to add to front or back of key list. If true then key is added to beginning and replace highest-priority key for encryption
  • dirty_config — if true then mark the config as dirty (incrementing seqno and needing a push) if the first key (i.e. the key used for encryption) is changed as a result of this call. Ignored if the config is not modifiable.

Returns

ConfigBase::clear_keys

Clears all stored encryption/decryption keys. This is typically immediately followed with one or more add_key call to replace existing keys. Returns the number of keys removed.

Declaration

int clear_keys(bool dirty_config = false);

Parameters

  • dirty_config — if this removes a key then mark the config as dirty (incrementing seqno and requiring a push). Only has an effect if the config is modifiable.

Returns

  • int — Returns number of keys removed

ConfigBase::compression_level

The zstd compression level to use for this type. Subclasses can override this if they have some particular special compression level, or to disable compression entirely (by returning std::nullopt). The default is zstd level 1.

Declaration

virtual std::optional<int> compression_level() const { return 1; }

Parameters

This endpoint takes no inputs.

Returns

  • std::optional<int> — Returns the compression level

ConfigBase::config_lags

How many config lags should be used for this object; default to 5. Implementing subclasses can override to return a different constant if desired. More lags require more "diff" storage in the config messages, but also allow for a higher tolerance of simultaneous message conflicts.

Declaration

virtual int config_lags() const { return 5; }

Parameters

This endpoint takes no inputs.

Returns

  • int — Returns how many config lags

ConfigBase::confirm_pushed

Should be called after the push is confirmed stored on the storage server swarm to let the object know the config message has been stored and, ideally, that the obsolete messages returned by push() are deleted. Once this is called needs_push will start returning false until something changes. Takes the seqno that was pushed so that the object can ensure that the latest version was pushed (i.e. in case there have been other changes since the push() call that returned this seqno).

Ideally the caller should have both stored the returned message and deleted the given messages. The deletion step isn't critical (it is just cleanup) and callers should call this as long as the store succeeded even if there were errors in the deletions.

It is safe to call this multiple times with the same seqno value, and with out-of-order seqnos (e.g. calling with seqno 122 after having called with 123; the duplicates and earlier ones will just be ignored).

Declaration

virtual void confirm_pushed(seqno_t seqno, std::string msg_hash);

Parameters

  • seqno — sequence number that was pushed
  • msg_hash — message hash that was pushed

Returns

ConfigBase::current_hashes

The current config hash(es); this can be empty if the current hash is unknown or the current state is not clean (i.e. a push is needed or pending).

Declaration

std::vector<std::string> current_hashes() const;

Parameters

This endpoint takes no inputs.

Returns

  • std::vector<std::string> — Returns current config hashes

ConfigBase::current_state_string()

Returns one of "clean", "pending", or "DIRTY" depending on the current state. This is primarily intended for logging.

Declaration

std::string_view current_state_string() const {

Parameters

This endpoint takes no inputs.

Returns

  • std::string_view — fixed string (clean, DIRTY, or pending) describing the current state.

ConfigBase::dump

Returns a dump of the current state for storage in the database; this value would get passed into the constructor to reconstitute the object (including the push/not pushed status). This method is not virtual: if subclasses need to store extra data they should set it in the subclass_data field. Resets the needs_dump() flag to false.

Declaration

ustring dump();

Parameters

This endpoint takes no inputs.

Returns

  • ustring — Returns binary data of the state dump

ConfigBase::encryption_domain

Subclasses must override this to return a constant string that is unique per config type; this value is used for domain separation in encryption. The string length must be between 1 and 24 characters; use the class name (e.g. "UserProfile") unless you have something better to use. This is rarely needed externally; it is public merely for testing purposes.

Declaration

virtual const char* encryption_domain() const = 0;

Parameters

This endpoint takes no inputs.

Returns

  • Namespace — Returns the namespace where config type is stored/loaded

ConfigBase::extra_data

Called when dumping to obtain any extra data that a subclass needs to store to reconstitute the object. The base implementation does nothing (i.e. extra data will be an empty dict). The counterpart to this, load_extra_data(), is called when loading from a dump that has extra data; a subclass should either override both (if it needs to serialize extra data) or neither (if it needs no extra data). Internally this extra data is stored in the "+" key of the dump.

Note that loading extra properly requires two-step construction: the subclass constructor must construct the ConfigBase object without extra data, and then call init_from_dump() from within its own constructor to load the dump. Failing to do this two-step initialization will result in the subclass load_extra_data not being called (because the subclass instance, and thus the overridden method, does not yet exist during the ConfigBase constructor).

Declaration

virtual void extra_data(oxenc::bt_dict_producer&&) const {}

Parameters

  • extra — An empty dict producer into which extra data can be added.

Returns

ConfigBase::get_keys

Returns a vector of encryption keys, in priority order (i.e. element 0 is the encryption key, and the first decryption key).

This method is mainly for debugging/diagnostics purposes; most config types have one single key (based on the secret key), and multi-keyed configs such as groups have their own methods for encryption/decryption that are already aware of the multiple keys.

Declaration

std::vector<ustring_view> get_keys() const;

Parameters

This endpoint takes no inputs.

Returns

  • std::vector<ustring_view> — Returns vector of encryption keys

ConfigBase::has_key

Returns true if the given key is already in the keys list.

Declaration

bool has_key(ustring_view key) const;

Parameters

  • key — will search if this key exists in the key list

Returns

  • bool — Returns true if it does exist

ConfigBase::is_clean

Returns true if we are currently clean (i.e. our current config is stored on the server and unmodified).

Declaration

bool is_clean() const { return _state == ConfigState::Clean; }

Parameters

This endpoint takes no inputs.

Returns

  • bool — Returns true if changes have been serialized

ConfigBase::is_dirty

Returns true if we are currently dirty (i.e. have made changes that haven't been serialized yet).

Declaration

bool is_dirty() const { return _state == ConfigState::Dirty; }

Parameters

This endpoint takes no inputs.

Returns

  • bool — Returns true if changes haven't been serialized

ConfigBase::is_readonly

Returns true if this config object is in read-only mode: specifically that means that this config object can only absorb new config entries but is incapable of producing new entries, and thus cannot modify or merge configs.

This currently happens for config messages that require verification of a signature but do not have the private keys required to produce a signature. For private config types, such as single-user configs, this will never be the case (as those can only be decrypted in the first place if you possess the private key). Note, however, that additional conditions for read-only could be added in the future, so this being true should not strictly be interpreted as a cannot-sign issue.

There are some consequences of being readonly:

  • any attempt to modify config values will throw an exception.
  • when multiple conflicting config objects are loaded only the "best" (i.e. higher seqno, with ties determined by hashed value) config is loaded; if values need to be merged this config will ignore the alternate values until someone who can produce a signature produces a merged config that properly incorporates (and signs) the updated config.
  • read-only configurations never have anything to push, that is, needs_push() will always be false.
  • it is still possible to push() a config anyway, but this only returns the current config and signature of the message currently being used, and never returns any obsolete hashes. Typically this is unlikely to be useful, as it is expected that only signers (who can update and merge) are likely also the only ones who can actually push new configs to the swarm.
  • read-only configurations do not reliably track obsolete hashes as the obsolescence logic depends on the results of merging, which read-only configs do not support. (If you do call push(), you'll always just get back an empty list of obsolete hashes).

Declaration

bool is_readonly() const { return _config->verifier && !_config->signer; }

Parameters

This endpoint takes no inputs.

Returns

  • bool true if this config object is read-only

ConfigBase::key

Accesses the key at position i (0 if omitted). There must be at least one key, and i must be less than key_count(). The key at position 0 is used for encryption; for decryption all keys are tried in order, starting from position 0.

Declaration

ustring_view key(size_t i = 0) const {

Parameters

  • i — keys position in key list

Returns

  • ustring_view — binary data of the key

ConfigBase::key_count

Returns the number of encryption keys.

Declaration

int key_count() const;

Parameters

This endpoint takes no inputs.

Returns

  • int — Returns number of encryption keys

ConfigBase::load_extra_data

Called when constructing from a dump with the extra data dict. The base implementation does nothing. See extra_data() for a description.

Declaration

virtual void load_extra_data(oxenc::bt_dict_consumer&&) {}

Parameters

  • extra — bt_dict_consumer over the extra data subdict.

Returns

ConfigBase::load_key

Called to load an ed25519 key for encryption; this is meant for use by single-ownership config types, like UserProfile, but not shared config types (closed groups).

Takes a binary string which is either the 32-byte seed, or 64-byte libsodium secret (which is just the seed and pubkey concatenated together), and then calls key(...) with the seed. Throws std::invalid_argument if given something that doesn't match the required input.

Declaration

void load_key(ustring_view ed25519_secretkey);

Parameters

  • ed25519_secret_key — key is loaded for encryption

Returns

ConfigBase::make_dump

Returns a dump of the current state; unlike dump() this does not update the internal needs_dump flag; it is mostly used internally (by dump()), but can also be called externally for debugging purposes.

Declaration

ustring make_dump() const;

Parameters

This endpoint takes no inputs.

Returns

  • ustring — Returns binary data of the state dump

ConfigBase::merge

This takes all of the messages pulled down from the server and does whatever is necessary to merge (or replace) the current values.

Values are pairs of the message hash (as provided by the server) and the raw message body.

For backwards compatibility, for certain message types (ones that have a accepts_protobuf() override returning true) optional protobuf unwrapping of the incoming message is performed; if successful then the unwrapped raw value is used; if the protobuf unwrapping fails, the value is used directly as a raw value.

After this call the caller should check needs_push() to see if the data on hand was updated and needs to be pushed to the server again (for example, because the data contained conflicts that required another update to resolve).

Returns the number of the given config messages that were successfully parsed.

Will throw on serious error (i.e. if neither the current nor any of the given configs are parseable). This should not happen (the current config, at least, should always be re-parseable).

Declaration

std::vector<std::string> merge(
    const std::vector<std::pair<std::string, ustring_view>>& configs);
std::vector<std::string> merge(
    const std::vector<std::pair<std::string, ustring>>& configs);

Parameters

  • configs — vector of pairs containing the message hash and the raw message body (or protobuf-wrapped raw message for certain config types).

Returns

  • vector of successfully parsed hashes. Note that this does not mean the hash was recent or that it changed the config, merely that the returned hash was properly parsed and processed as a config message, even if it was too old to be useful (or was already known to be included). The hashes will be in the same order as in the input vector.

ConfigBase::needs_dump

Returns true if something has changed since the last call to dump() that requires calling and saving the dump() data again.

Declaration

virtual bool needs_dump() const { return _needs_dump; }

Parameters

This endpoint takes no inputs.

Returns

  • bool — Returns true if something has changed since last call to dump

ConfigBase::needs_push

Returns true if this object contains updated data that has not yet been confirmed stored on the server. This will be true whenever is_clean() is false: that is, if we are currently "dirty" (i.e. have changes that haven't been pushed) or are still awaiting confirmation of storage of the most recent serialized push data.

Declaration

virtual bool needs_push() const;

Parameters

This endpoint takes no inputs.

Returns

  • bool — Returns true if the object needs pushing

ConfigBase::old_hashes

The old config hash(es); this can be empty if there are no old hashes or if the config is in a dirty state (in which case these should be retrieved via the push function). Calling this function or the push function will clear the stored old_hashes.

Declaration

std::vector<std::string> old_hashes();

Parameters

This endpoint takes no inputs.

Returns

  • std::vector<std::string> — Returns old config hashes

ConfigBase::push

Returns a tuple of three elements: - the seqno value of the data - the data message to push to the server - a list of known message hashes that are obsoleted by this push.

Additionally, if the internal state is currently dirty (i.e. there are unpushed changes), the internal state will be marked as awaiting-confirmation. Any further data changes made after this call will re-dirty the data (incrementing seqno and requiring another push).

The client is expected to send a sequence request to the server that stores the message and deletes the hashes (if any). It is strongly recommended to use a sequence rather than a batch so that the deletions won't happen if the store fails for some reason.

Upon successful completion of the store+deletion requests the client should call confirm_pushed with the seqno value to confirm that the message has been stored.

Subclasses that need to perform pre-push tasks (such as pruning stale data) can override this to prune and then call the base method to perform the actual push generation.

Declaration

virtual std::tuple<seqno_t, ustring, std::vector<std::string>> push();

Parameters

This endpoint takes no inputs.

Returns

  • std::tuple<seqno_t, ustring, std::vector<std::string>> - Returns a tuple containing
  • seqno_t — sequence number
  • ustring — data message to push to the server
  • std::vector<std::string> — list of known message hashes

ConfigBase::remove_key

Removes the given encryption/decryption key, if present. Returns true if it was found and removed, false if it was not in the key list.

The optional second argument removes the key only from position from or higher. It is mainly for internal use and is usually omitted.

Declaration

bool remove_key(ustring_view key, size_t from = 0, bool dirty_config = false);

Parameters

  • key — the key to remove from the key list
  • from — optional agrument to specify which position to remove from, usually omitted
  • dirty_config — if true, and the first key (the encryption key) is removed from the list then mark the config as dirty (incrementing seqno and requiring a re-push). Ignored if the config is not modifiable.

Returns

  • bool — Returns true if found and removed

ConfigBase::replace_keys

Replaces the full set of keys with the given vector of keys. This is equivalent to calling clear_keys() and then add_key with the keys, in order (and so the first key in the vector becomes the highest priority, i.e. the key used for encryption).

Declaration

void replace_keys(const std::vector<ustring_view>& new_keys, bool dirty_config = false);

Parameters

  • new_keys — the new decryption keys; the first key becomes the new encryption key
  • dirty_config — if true then set the config status to dirty (incrementing seqno and requiring a repush) if the old and new first key are not the same. Ignored if the config is not modifiable.

Returns

ConfigBase::storage_namespace

Accesses the storage namespace where this config type is to be stored/loaded from. See namespaces.hpp for the underlying integer values.

Declaration

virtual Namespace storage_namespace() const = 0;

Parameters

This endpoint takes no inputs.

Returns

  • Namespace — Returns the namespace where config type is stored/loaded

ConfigSig::clear_sig_keys

Drops the signature pubkey and/or secret key, if the object has them.

Declaration

void clear_sig_keys();

Parameters

This endpoint takes no inputs.

Returns

ConfigSig::get_sig_pubkey

Returns a const reference to the 32-byte Ed25519 signing pubkey, if set.

Declaration

const std::optional<std::array<unsigned char, 32>>& get_sig_pubkey() const { return _sign_pk; }

Parameters

This endpoint takes no inputs.

Returns

  • reference to the 32-byte pubkey, or std::nullopt if not set.

ConfigSig::set_sig_keys

Sets an Ed25519 keypair pair for signing and verifying config messages. When set, this adds an additional signature for verification into the config message (after decryption) that validates a config message.

This is used in config contexts where the encryption/decryption keys are insufficient for permission verification to produce new messages, such as in groups where non-admins need to be able to decrypt group data, but are not permitted to push new group data. In such a case only the admins have the secret key with which messages can be signed; regular users can only read, but cannot write, config messages.

When a signature public key (with or without a secret key) is set the config object enters a "signing-required" mode, which has some implications worth noting: - incoming messages must contain a signature that verifies with the public key; messages without such a signature will be dropped as invalid. - because of the above, a config object cannot push config updates without the secret key: thus any attempt to modify the config message with a pubkey-only config object will raise an exception.

Declaration

void set_sig_keys(ustring_view secret);

Parameters

  • secret — the 64-byte sodium-style Ed25519 "secret key" (actually the seed+pubkey concatenated together) that sets both the secret key and public key.

Returns

ConfigSig::set_sig_pubkey

Sets a Ed25519 signing pubkey which incoming messages must be signed by to be acceptable. This is intended for use when the secret key is not known (see set_sig_keys() to set both secret and pubkey keys together).

Declaration

void set_sig_pubkey(ustring_view pubkey);

Parameters

  • pubkey — the 32 byte Ed25519 pubkey that must have signed incoming messages

Returns